[ 정적팩토리메소드 패턴 ]

https://7942yongdae.tistory.com/147

 

[ 정적팩토리메소드 패턴 네이밍 룰 ]

1. from: 하나의 매개변수를 받아서 해당 타입의 인스턴스 생성
2. of: 여러개의 매개변수를 받아서 인스턴스를 생성
3. instance or getInstance: 인스턴스를 반환하지만 동일한 인스턴스임을 보장하지 않는다.
4. create or newInstance: instance 혹은 getInstance와 같지만, 매번 새로운 인스턴스를 생성하여 반환함을 보장.
5. getType: getInstance와 같으나 생성할 클래스가 아닌 다른 클래스에 팩토리 메소드를 정의할 때 사용. (호출하는 클래스와 다른 타입의 인스턴스를 반환할때 사용)

FileStore fs = Files.getFileStore(path);

6. newType: getType과 같지만 매번 새로운 인스턴스를 반환

7. type : getType 과 newType의 간결한 버전

List<Test> list = Collections.list(test);

https://velog.io/@saint6839/%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C-%EB%84%A4%EC%9D%B4%EB%B0%8D-%EB%B0%A9%EC%8B%9D

 

[ 생성자에 코드를 넣지 말자 ]

https://cozzin.tistory.com/71

 

※ 디미터법칙

https://tecoble.techcourse.co.kr/post/2020-06-02-law-of-demeter/

1. 객체 자신의 메서드들

2. 메서드의 파라미터로 넘어온 객체들의 메서드들

3. 메서드 내부에서 생성, 초기화된 객체의 메서드들

4. 인스턴스 변수로 가지고 있는 객체가 소유한 메서드들

 

 

반응형

팩토리 패턴

팩토리라는 클래스에 객체 생성을 위임(캡슐화)하여 팩토리 클래스가 객체를 생성하도록 하는 방식.

어떤 클래스의 인스턴스를 생성할지 서브클래스에서 결정하도록 한다는게 팩토리 패턴의 핵심.

분류에 딱히 큰 의미는 없는 듯 하나, 팩토리 메소드 패턴과 추상 팩토리 메소드 패턴으로 나누고 있다.

"구상클래스에 의존하지 않게, 추상화 된 것에 의존하도록 개발" 을 따르는 패턴

 

 

1. 팩토리 메소드 패턴

: 객체 생성을 담당하는 팩토리 메소드 작성하여 객체 생성을 캡슐화

: 객체를 생성하기 위한 인터페이스를 정의하는데, 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정한다.

 

[Mouse interface]

1
2
public interface Mouse {
}
cs

[LGMouse.java]

Mouse interface 구현

1
2
3
4
5
public class LGMouse implements Mouse {
    public LGMouse() {
        System.out.println("LG 마우스 생성");
    }
}
cs

[SamsungMouse.java]

Mouse interface 구현

1
2
3
4
5
public class SamsungMouse implements Mouse {
    public SamsungMouse() {
        System.out.println("Samsung 마우스 생성");
    }
}
cs

 

[Factory.java]

객체 생성을 맡아서 처리 하는 팩토리클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Factory {
    public static Mouse createMouse(String type) {
        Mouse mouse = null;
        switch(type) {
            case "LG":
                mouse = new LGMouse();
                break;
            case "SAMSUNG":
                mouse = new SamsungMouse();
                break;
        }
        return mouse;
    }
}
cs

 

[Client]

1
2
3
4
5
public class Client {
    public static void main(String[] args) {
        Factory.createMouse("LG");
    }
}
cs

[결과]

LG 마우스 생성

 

 

2. 추상 팩토리 패턴

: 연관된 서브 클래스를 그룹화할 수 있고 그룹을 자유롭게 교체할 수 있는 패턴

: 인터페이스를 이용하여 서로 연관된, 또는 의존하는 객체를 구상 클래스를 지정하지 않고 생성할 수 있다

: 팩토리 메소드 패턴이 좀 더 캡슐화 되어있는 형태

 

[Mouse interface]

1
2
public interface Mouse {
}
cs

[LGMouse.java]

Mouse interface 구현

1
2
3
4
5
public class LGMouse implements Mouse {
    public LGMouse() {
        System.out.println("LG 마우스 생성");
    }
}
cs

[SamsungMouse.java]

Mouse interface 구현

1
2
3
4
5
public class SamsungMouse implements Mouse {
    public SamsungMouse() {
        System.out.println("SAMSUNG 마우스 생성");
    }
}
cs

 

[Keyboard interface]

1
2
public interface Keyboard {
}
cs

[LGKeyboard.java]

Keyboard interface 구현

1
2
3
4
5
public class LGKeyboard implements Keyboard {
    public LGKeyboard() {
        System.out.println("LG 키보드 생성");
    }
}
cs

[SamsungKeyboard.java]

Keyboard interface 구현

1
2
3
4
5
public class SamsungKeyboard implements Keyboard {
    public SamsungKeyboard() {
        System.out.println("SAMSUNG 키보드 생성");
    }
}
cs

 

[Computer interface]

1
2
3
4
public interface Computer {
    public Keyboard createKeyboard();
    public Mouse createMouse();
}
cs

[ComputerFactory.java]

Computer interface 구현

1
2
3
4
5
6
public class ComputerFactory {
     public void createComputer(Computer computer){
        computer.createKeyboard();
        computer.createMouse();
    }
}
cs

 

[Client.java]

1
2
3
4
5
6
7
public class Client {
    public static void main(String[] args) {
        ComputerFactory cf = new ComputerFactory();
        cf.createComputer(new SamsungComputer());
        cf.createComputer(new LGComputer());
    }
}
cs

[결과]

SAMSUNG 컴퓨터 생성
SAMSUNG 키보드 생성
SAMSUNG 마우스 생성
LG 컴퓨터 생성
LG 키보드 생성
LG 마우스 생성

 

참고 :

아래의 포스팅을 많이 참고했습니다. 훌륭한 예제를 보시려면 아래를 참고해주세요..

https://victorydntmd.tistory.com/300

 

 

 

반응형

어댑터 패턴은 실생활에서 사용하는 어댑터와 동일한 역할을 수행한다.

한 클래스의 인터페이스를 클라이언트에서 사용하고자 하는 다른 인터페이스로 변환한다.

 

어댑터 패턴 사용시 라이브러리를 직접 사용하는 것이 아닌, 

라이브러리를 캡슐화한 어댑터를 사용하게 되므로, 소스간 결합도가 약해지는 장점이 있다.

wrapper 와 slf4j(log4j 의 어댑터) 는 어댑터라 볼 수 있다.

 

 

[Adapter Pattern의 Class 다이어그램(참고)]

Client 는 Adapter 구상 클래스를 Target Interface를 통해 사용한다.

Adapter 클래스는 Target Interface를 구현한다.

Adapter 클래스는 Adaptee(새롭게 사용할 구상 클래스) 를 구상 클래스로 사용하며,

Adapter의 Request()는 Adaptee의 SpecificRequest()를 호출한다. (Adaptee를 캡슐화)

Client 는 Adapter 의 Request()를 사용하는 줄 알지만 실제론 Adaptee 의 SpecificRequest()를 사용하게 된다.

 

실제 Adaptor pattern 의 사용 예를 살펴보자.

 

 

1-1. Adapter 패턴을 사용하지 않은 설계

[OldLibraryIF]

1
2
3
public interface OldLibraryIF {
    public void sendPush(String input);
}
cs

 

[OldLibrary]

OldLibrary 인터페이스를 구현하는 클래스

1
2
3
4
5
6
7
public class OldLibrary implements OldLibraryIF {
    
    public void sendPush(String input) {
        System.out.println("send " + input + " using OldLibrary");
    }
    
}
cs

 

[Client]

OldLibrary를 사용하는 클라이언트

1
2
3
4
5
6
public class Client {
    public static void main(String[] args) {
       OldLibraryIF oldLib = new OldLibrary();
       oldLib.sendPush("message");
    }
}
cs

 

[결과]

send message using OldLibrary

위와 같이 모든 구현이 끝나있는 상태에서 기존의 라이브러리였던 OldLibrary 를 제거하고,

아래와 같은 새로운 라이브러리(NewLibrary)를 사용해야 하는 상황이 생긴다면 어떻게 해야할까?

교체대상인 OldLibrary를 사용하는 클라이언트를 일일히 찾아 수정해주어야 한다.

 

1-2. Adapter 패턴을 사용하지 않은 설계에서 OldLibrary를 NewLibrary로 교체

[NewLibraryIF]

1
2
3
4
public interface NewLibraryIF {
    public void sendMsg(String input);
}
 
cs

 

[NewLibrary]

NewLibrary 인터페이스를 구현하는 클래스

1
2
3
4
5
6
7
public class NewLibrary implements NewLibraryIF {
    
    public void sendMsg(String input) {
        System.out.println("send " + input + " using NewLibrary");
    }
    
}
cs

 

[Client]

OldLibrary 대신 NewLibrary를 사용하도록 기존의 Client 를 수정.

1
2
3
4
5
6
7
public class Client {
    public static void main(String[] args) {
        NewLibraryIF newLib = new NewLibrary();
        newLib.sendMsg("message");
    }
}
 
cs

 

[결과]

send message using NewLibrary

라이브러리 교체는 쉽게 끝이난 듯 보인다.

하지만 OldLibrary를 사용하는 곳이 수십, 수백곳이라면?!

애초에 어댑터 패턴을 사용했더라면 어땟을까?

 

2-1. Adapter 패턴을 사용한 설계

[Adapter]

OldLibrary를 캡슐화한 Adapter 클래스

1
2
3
4
5
6
7
8
9
10
public class Adapter implements OldLibraryIF{
    
    OldLibraryIF oldLibrary = new OldLibrary();
 
    @Override
    public void sendPush(String input) {
        oldLibrary.sendPush(input);
    }
    
}
cs

 

[Client]

1
2
3
4
5
6
public class Client {
    public static void main(String[] args) {
        OldLibraryIF adapter = new Adapter();
        adapter.sendPush("message");
    }
}
cs

 

[결과]

send message using OldLibrary

Client 에서 OldLibrary를 생성자로 직접 생성하여 사용하는 대신, Adapter 클래스를 사용했다.

이전과 마찬가지로 OldLibrary를 NewLibrary로 교체해보자.

 

2-2. Adapter 패턴을 사용한 설계에서 OldLibrary를 NewLibrary로 교체

[Adapter]

OldLibrary를 멤버변수로 사용하는 대신 NewLibrary를 멤버변수로 사용

1
2
3
4
5
6
7
8
9
10
11
public class Adapter implements OldLibraryIF{
    
    NewLibraryIF newLibrary = new NewLibrary();
 
    @Override
    public void sendPush(String input) {
        newLibrary.sendMsg(input);
    }
    
}
 
cs

 

[Client]

기존의 Client 코드와 동일한 코드를 사용해도 된다.

Adapter 클래스 코드만 수정했을 뿐, Client 코드는 바꾼게 없다.

1
2
3
4
5
6
7
public class Client {
    public static void main(String[] args) {
        OldLibraryIF adapter = new Adapter();
        adapter.sendPush("message");
    }
}
 
cs

 

[결과]

send message using NewLibrary

이와 같이 Adapter 패턴(캡슐화)을 사용하게 되면 라이브러리의 변화가 발생해도 클라이언트를 수정하지 않아도 된다.

도서 <Clean Code> 에도 외부라이브러리를 캡슐화 하여 사용하는 이점에 대해 언급되어있다.

 

 

참고 : Head First Design Patterns

반응형

1. Telescoping Pattern (텔레스코핑 패턴)

생성자와 오버로딩을 사용하는 패턴

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package design_pattern.builder;
public class CoordinateTelescoping {
    //텔레스코핑 생성자 패턴
    //Telescoping constructor
 
    private final int x;
    private final int y;
    private int w;
    private int h;
    
    public CoordinateTelescoping(int x, int y) {
        this(x, y, 00);
    }
    public CoordinateTelescoping(int x, int y, int w, int h) {
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
    }
    
}
 
cs

매개변수 증가시 코드량이 많아지고 무거워지는 문제,

초기화가 필요 없는 값도 무조건적으로 넘겨야 하는 경우가 존재

 

2. JavaBean Pattern (자바빈 패턴)

getter, setter 사용하는 패턴

(일반적으로 많이 사용되는 Value Object)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package design_pattern.builder;
public class CoordinateJavaBean {
    //자바 빈 패턴
    //java bean pattern
    
    private int x=0;
    private int y=0;
    private int w=0;
    private int h=0;
    
    public void setX(int x) {
        this.x = x;
    }
    public void setY(int y) {
        this.y = y;
    }
    public void setW(int w) {
        this.w = w;
    }
    public void setH(int h) {
        this.h = h;
    }
    
}
 
cs

객체가 완전하게 생성되었는지 보장 불가(동결(freezing) 보장 불가)

CoordinateJavaBean b = CoordinateJavaBean();
b.setX(10);

위 경우 x 값만 set한 채 객체를 사용할 수 있으므로 객체가 완전하게 생성되지 않은 상태

 

3. Builder Pattern (빌더 패턴)

텔레스코핑패턴 + 자바빈 패턴의 형태

초기화가 반드시 필요한 멤버변수는 생성자로,

선택적으로 초기화를 해도 되는 필드는 setter 메소드로 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package design_pattern.builder;
 
public class CoordinateBuilder {
    //빌더 패턴
    //Builder pattern : 텔레스코핑 패턴 + 자바빈 패턴
 
    private final int x;
    private final int y;
    private final int w;
    private final int h;
    
    public static class Builder {
        private final int x;
        private final int y;
        private int w;
        private int h;
        
        public Builder(int x, int y) {
            this.x = x;
            this.y = y;
        }
        
        public Builder setW(int w) {
            this.w = w;
            return this;
        }
        
        public Builder setH(int h) {
            this.h = h;
            return this;
        }
        
        public CoordinateBuilder build() {
            return new CoordinateBuilder(this);
        }
    }
    
    public CoordinateBuilder(Builder builder) {
        x = builder.x;
        y = builder.y;
        w = builder.w;
        h = builder.h;
    }
    
}
 
cs

[객체 생성]

CoordinateBuilder c = new CoordinateBuilder.Builder(10, 10).setW(5).setH(5).build();

생성자로 필수 맴버변수를 초기화 하는 객체 생성, 선택적으로 초기화가 필요한 멤버변수는 setter 사용,
마지막으로 build 를 호출하여 객체 리턴

 

 

참고 :

https://using.tistory.com/71

 

 

반응형

원래 객체의 기능을 다른 객체가 대신 처리하도록 설계하는 패턴

AOP 등에 사용되는 패턴

 

 

Proxy 기본 패턴

[Subject interface]

1
2
3
public interface Subject {
    public String action();
}
cs

 

[RealSubject class]

실제 사용하는 객체

1
2
3
4
5
6
7
8
public class RealSubject implements Subject{
 
    @Override
    public String action() {
        return "Real Subject action()";
    }
}
 
cs

 

[Proxy class]

프록시 객체.

내부적으로 RealSubject 객체를 생성하여 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Proxy implements Subject {
    
    RealSubject realSubject;
    
    @Override
    public String action() {
        if(realSubject == null) {
            realSubject = new RealSubject();
        }
        return realSubject.action();
    }
}
 
cs

 

[Client class]

1
2
3
4
5
6
7
8
9
public class Client {
 
    public static void main(String[] args) {
        Subject subject = new Proxy();
        System.out.println(subject.action());
    }
 
}
 
cs

 

 

Proxy의 실제 사용

[AOP 흉내내기]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Proxy implements Subject {
    
    RealSubject realSubject;
    
    @Override
    public Object action() {
        if(realSubject == null) {
            realSubject = new RealSubject();
        }
        
        preProcess();
        Object result = realSubject.action();
        postProcess();
        
        return result;
    }
    
    private void preProcess() {
        System.out.println("선행작업");
    }
    
    private void postProcess() {
        System.out.println("사후작업");
    }
}
 
cs

 

[실제 AOP 소스]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@Component
@Aspect
public class ControllerAOP {
 
    protected static final Logger logger = LoggerFactory.getLogger(ControllerAOP.class);
    private final String CANNOT_PRINT_INPARAM = "{IN_PARAMS LENGTH IS TOO LONG TO PRINT}";
    private final String CANNOT_PRINT_OUTPARAM = "{OUT_PARAMS LENGTH IS TOO LONG TO PRINT}";
    private final String CANNOT_PRINT_VALUE = "VALUE LENGTH IS TOO LONG TO PRINT";
    private final String NOTHING_TO_PRINT = "RETURN TYPE IS VOID";
    
    @Around("execution(public void com.sample..*Controller.*(..))")
    public void voidAround(ProceedingJoinPoint pjp) {
        
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
 
        String inputParam = null;
        
        for (Object obj : pjp.getArgs()) {
            if (obj instanceof Map) {
                inputParam = obj.toString();
            } else if (obj instanceof String){
                inputParam = (String)obj;
            }
        }
 
        long start = System.currentTimeMillis();
 
        String controller = (String) pjp.getTarget().getClass().getSimpleName();
 
        String path = request.getRequestURI();
        String addr = request.getRemoteAddr();
        int port = request.getRemotePort();
 
        logger.info("##########################################################################");
        logger.info("# REQUEST | CONTROLLER = {} | METHOD = {} | REMOTEADDR = {} | PORT = {} | IN_PARAMS = {}",
                controller, path, addr, port, inputParam==null?"":(inputParam.length()>=1500?this.CANNOT_PRINT_INPARAM:inputParam));
        logger.info("##########################################################################");
        
        String result_msg = "success";
        
        try {
            pjp.proceed();
            result_msg = "success";
        } catch (APIException e) {
            result_msg = "fail";
        } catch (Throwable e) {
            result_msg = "fail";
        } finally {
            long end = System.currentTimeMillis();
            logger.info("##########################################################################");
            logger.info("# RESPONSE | CONTROLLER = {} | METHOD = {} | RESULT = {} | REMOTEADDR = {} | PORT = {} | TIME = {} ms | IN_PARAMS = {} | OUT_PARAMS = {}"
                    controller, path, result_msg, addr, port, end-start, inputParam==null?"":(inputParam.length()>=1500?this.CANNOT_PRINT_INPARAM:inputParam), 
                    this.NOTHING_TO_PRINT);
            logger.info("##########################################################################");
        }
 
 
    }
cs

위 AOP 코드 요약 : joinpoint(클라이언트에서 호출되는 모든 메소드) 중에서 pointcut으로(execution) 잡은 특정 메소드만 잡아 공통 관심사로 (advice(@Around)) 로 처리.

 

실제 호출되는 메소드를 인자(ProceedingJoinPoint)로 받아 프록시 패턴과 같이 처리한다.

pjp.proceed() 가 실제 메소드 실행부이며,

pjp.proceed() 위 아래로 로그를 처리하는 코드가 있다.

 

 

참고 :

도서 Head first design patterns,

https://effectiveprogramming.tistory.com/entry/Proxy-%ED%8C%A8%ED%84%B4%EA%B3%BC-%EA%B7%B8-%ED%99%9C%EC%9A%A9

 

반응형

옵저버 패턴에서는 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다 의존성을 정의한다. (Head First Design Pattern 내용 발췌)

Subject(주제) 객체에 변화가 일어날 경우 Subject에 의존하고 있는 다수의 Observer 에 변화를 알리는 일대다 의존성을 갖는 디자인 패턴. 대표적으로 java.awt 의 listener가 Observer pattern 이라고 할 수 있다.

 

Observer pattern 을 파악하기 위해

java.awt 의 lister 와 Button 을 어설프게나마 직접 구현해보자.

 

[Listener]

Observer 와 같은 역할을 하는 Listener Interface

1
2
3
4
5
6
package design_pattern.observer;
 
public interface Listener {
    public void performAction();
}
 
cs

 

[CustomListener1]

Observer 를 구현한 CustomListener1

performAction() 은 주제 객체의 변화가 일어났을 때 호출이 되는 메소드

1
2
3
4
5
6
7
8
9
package design_pattern.observer;
 
public class CustomListener1 implements Listener {
    @Override
    public void performAction() {
        System.out.println("custom listener1 perform action !");
    }
}
 
cs

 

[Component]

주제 인터페이스 Component

1
2
3
4
5
6
7
8
package design_pattern.observer;
 
public interface Component {
    public void addListener(Listener l);
    public void removeListener(Listener l);
    public void notifyListeners();
}
 
cs

 

[Button]

주제 인터페이스를 구현한 주제 객체 Button

Observer 역할을 하는 listeners 를 멤버변수로 갖고 있다(구상)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package design_pattern.observer;
 
import java.util.ArrayList;
 
public class Button implements Component {
    
    private ArrayList<Listener> listeners;
    
    public Button() {
        this.listeners = new ArrayList<Listener>();
    }
    
    @Override
    public void addListener(Listener l) {
        listeners.add(l);
        System.out.println("listener added!");
    }
 
    @Override
    public void removeListener(Listener l) {
        int idx = listeners.indexOf(l);
        listeners.remove(idx);
        System.out.println("listener removed!");
    }
 
    @Override
    public void notifyListeners() {
        for(Listener l : listeners) {
            l.performAction();
        }
    }
    
    public void buttonClicked() {
        notifyListeners();
    }
    
}
 
cs

 

[Main]

Listener 를 익명클래스로 구현 후 customListeners2 변수로 선언하였다.

Listener(Observer) 두개를 Button(주제) 객체에 등록(addListener)해주었고,

실제 Button 을 클릭 해 볼 수 없으니,

마치 버튼을 클릭 한 것 처럼 버튼객체의 buttonClicked() 메소드를 호출해준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package design_pattern.observer;
 
import java.awt.TextField;
import java.awt.event.ActionListener;
 
public class Main {
 
    public static void main(String[] args) {
        
        Button btn = new Button();
        
        Listener customListener1 = new CustomListener1();
        
        Listener customListener2 = new Listener() {
            @Override
            public void performAction() {
                System.out.println("custom listener2 perform action !");
            }
        };
        
        btn.addListener(customListener1);
        btn.addListener(customListener2);
        
        btn.buttonClicked();
        
        System.out.println("----------------------------");
        
        btn.removeListener(customListener2);
        
        System.out.println("----------------------------");
        btn.buttonClicked();
        
    }
    
}
 
cs

 

실행 결과

listener added!
listener added!
custom listener1 perform action !
custom listener2 perform action !
----------------------------
listener removed!
----------------------------
custom listener1 perform action !

 

반응형

"어떤 서브시스템의 일련의 인터페이스에 대한 통합된 인터페이스 제공.

퍼사드에서 고수준 인터페이스를 정의하기 때문에 서브시스템을 더 쉽게 사용할 수 있다."

 

사용할 메소드가 여러 클래스에 퍼져 있는 경우

필요한 클래스들을 모두 생성자로 생성하여 필요한 메소드를 각각 호출 하는 대신,

(최소 지식 원칙 (의존하는 클래스의 갯수를 최대한 적게) 위배)

별도의 클래스(퍼사드)에 사용할 클래스들을 멤버변수로 담고,

사용하고자 하는 멤버변수의 메소드들을 한곳에 묶은 새로운 메소드를 작성한다.

 

예를 들어, 메일 전송 로직을 다음과 같이 구현한다고 하자.

1. 수신인의 메일주소를 확인한다.

2. 메일을 전송한다.

3. 메일 전송 결과를 이력에 남긴다.

위 기능들이 각각 다른 클래스에 존재하는 메소드라고 한다면

위 세 기능을 묶은 하나의 메소드를 작성하여, 메인시스템에선 해당 메소드(서브시스템)만 호출한다.

 

[ValidationService class]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package design_pattern.fasade;
 
public class ValidationService {
    
    String mailAddr = "";
    
    public ValidationService(String addr){
        this.mailAddr = addr;
    }
    
    public boolean addrChk() {
        if(!mailAddr.isEmpty()) {
            System.out.println("mail validation check success!");
            return true;
        }
        return false;
    }
    
}
 
cs

 

[MailService class]

1
2
3
4
5
6
7
8
package design_pattern.fasade;
 
public class MailService {
    public void mailSend() {
        System.out.println("mail has been sent");
    }
}
 
cs

 

[HstService class]

1
2
3
4
5
6
7
8
package design_pattern.fasade;
 
public class HstService {
    public void hstSave() {
        System.out.println("sent mail saved");
    }
}
 
cs

 

[Facade.class]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package design_pattern.fasade;
 
public class Facade {
    private ValidationService vs;
    private MailService ms;
    private HstService hs;
    
    public Facade(ValidationService vs, MailService ms, HstService hs) {
        this.vs = vs;
        this.ms = ms;
        this.hs = hs;
    }
    
    public void facadeFunc() {
        if(vs.addrChk()) {
            ms.mailSend();
        } 
        hs.hstSave();
    }
}
 
cs

주시스템에서 사용하고자 하는 서브시스템들을 멤버변수로.

멤버변수 내에서 사용할 메소드들을 따로 모은, 새로운 메소드 구현(facadeFunc())

 

[main]

1
2
3
4
5
6
7
8
9
10
11
12
13
package design_pattern.fasade;
 
public class Main {
    
    public static void main(String args[]) {
        Facade f = new Facade(new ValidationService("receiver@gmail.com")
                            , new MailService()
                            , new HstService());
        f.facadeFunc();
    }
    
}
 
cs

 

디자인 패턴 중 가장 단순한(이해하기 쉬운..) 구조이며 가장 많이 쓰이는 패턴 중 하나

반응형

1. 변화하는 기능을 별도의 interface로 분리(캡슐화)

2. 각각의 기능(interfaceImple)들이 인터페이스를 구현(implements)

3. 기능을 사용하는 객체(class) 내에서 기능(interface)을 멤버변수로 포함

4. 기능을 사용하는 객체(class) 내에서 기능(interfaceImple)을 주입받는 setter를 선언하여 각각의 객체(class)가 다른 기능(interfaceImple) 을 사용할 수 있게 설계

 

* 무조건적인 상속은 소스의 중복을 야기, 소스 수정시 구현체들의 소스 수정을 야기

 

[ex]

[Shout interface]

소리지르기 비명지르기 기능이 있는 Shout 인터페이스 작성.

1
2
3
4
5
6
7
package design_pattern.strategy;
 
public interface Shout {
    public void yell();
    public void scream();
}
 
cs

 

[Human class]

Shout 인터페이스를 구현한 Human 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package design_pattern.strategy;
 
public class Human implements Shout{
    
    @Override
    public void yell() {
        System.out.println("으악");
    }
 
    @Override
    public void scream() {
        System.out.println("꺄");
    }
    
}
 
cs

 

[Dog class]

Shout 인터페이스를 구현한 Dog 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package design_pattern.strategy;
 
public class Dog implements Shout{
 
    @Override
    public void yell() {
        System.out.println("왈");
    }
 
    @Override
    public void scream() {
        System.out.println("왈");
    }
    
}
 
cs

 

[Throat class]

소리지르기, 비명지르기 기능을 갖고 있는 Shout 인터페이스를 멤버변수로 두고 있는 Throat 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package design_pattern.strategy;
 
public class Throat {
    
    Shout s;
    
    public void setThroat(Shout s) {
        this.s = s;
    }
    
    public void func1() {
        s.yell();
    }
    
    public void func2() {
        s.scream();
    }
    
}
 
cs

 

[Main class(client)]

기능을 사용하는 클라이언트

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package design_pattern.strategy;
 
public class Main {
    public static void main(String args[]) {
        
        Throat throat = new Throat();
        
        throat.setThroat(new Human());
        throat.func1();
        throat.func2();
        
        System.out.println("------------------");
        
        throat.setThroat(new Dog());
        throat.func1();
        throat.func2();
        
    }
}
 
cs

 

[결과]

클라이언트 단에서 setter를 사용하여 클래스(기능)를 변경하여 사용 가능하다.

 

정리가 잘 되어있는 글

참고 도서

 

 

반응형

+ Recent posts