코딩 기록들

스프링 입문 2.3 옵저버(Observer), 파사드(facade), 전략(strategy) 패턴 본문

스프링 입문

스프링 입문 2.3 옵저버(Observer), 파사드(facade), 전략(strategy) 패턴

코딩펭귄 2023. 12. 31. 19:55

옵저버(Observer) 패턴

- 관찰자패턴

- 스윙, 자바의GUI프로그래밍, 안드로이드 프로그래밍 시 이벤트리스너가 예시(이벤트리스너를 통해 전달)

- 변화가 일어났을때관찰(관찰에 변화가생기면) 미리 등록된 다른클래스에 통보해주는 패턴을 구현한것

public class Main {
    public static void main(String[] args) {

        Button button = new Button("버튼");

        //익명으로 전달받음
        button.addListener(new IButtonListener() {
            public void clickEvent(String event){
                System.out.println(event);
            }
        });
        
        button.click('메시지 전달 : click 1');
        button.click('메시지 전달 : click 2');
        button.click('메시지 전달 : click 3');
    }
}
public interface IButtonListener {
    void clickEvent(String event);
}
public class Button {
    private String name;
    private IButtonListener buttonListener;

    public Button(String name){
        this.name = name;
    }

    public void click(String message){
        buttonListener.clickEvent(message );
    }

    public void addListener(IButtonListener buttonListener){
        this.buttonListener = buttonListener;
    }
}

 

 

파사트 패턴

- 건물의 앞쪽 정면 이라는 뜻. 건물의 뒤쪽에는 뭐가있는지 모르는 상태

- 여러 객체와 실제 사용하는 서브객체 사이에 복잡한 의존관계가 있을때, 중간에 facade라는 객체를 두고, 여기서 제공하는 인터페이스만을 활용하여 기능을 사용하는 방식 (여러개 객체를 합쳐 특정 기능 만들때 사용)

- 자신이 가지고있는 각 클래스의 기능을 명확히 알아야 함

public class Reader {

    private String fileName;

    public Reader(String fileName){
        this.fileName = fileName;
    }

    public void fileConnect(){
        String msg = String.format("Reader %s 로 연결합니다.", fileName);
        System.out.println(msg);
    }

    public void fileRead(){
        String msg = String.format("Reader %s 의 내용을 읽어옵니다.", fileName);
        System.out.println(msg);
    }

    public void fileDisconnect(){
        String msg = String.format("Reader %s 로 연결 종료 합니다.", fileName);
        System.out.println(msg);
    }
}
public class Writer {

    private String fileName;

    public Writer(String fileName){
        this.fileName = fileName;
    }

    public void fileConnect(){ // 생성, 이어쓰기
        String msg = String.format("Writer %s 로 연결합니다.", fileName);
        System.out.println(msg);
    }

    public void write(){
        String msg = String.format("Writer %s 로 파일쓰기를 합니다.", fileName);
        System.out.println(msg);
    }

    public void fileDisconnect(){
        String msg = String.format("Writer %s 로 연결 종료합니다.", fileName);
        System.out.println(msg);
    }
}
public class Ftp {
    private String host;
    private int port;
    private String path;

    public Ftp(String host, int port, String path){
        this.host = host;
        this.port = port;
        this.path = path;
    }

    public void connect(){ // 원격지에 커넥트
        System.out.println("FTP Host : " + host+ "Port : " +port+"로 연결합니다."")
    }

    public void moveDirectory(){ //해당 디렉토리로 이동
        System.out.println("FTP path : " + path+ "로 이동합니다.");

    }
    public void disConnect(){ //FTP연결 종료
        System.out.println("FTP연결을 종료합니다..");

    }
}
public class Main {
    public static void main(String[] args) {

        Ftp ftpClient = new Ftp("www.foo.co.kr", 22, "./home/etc");
        ftpClient.connect();
        ftpClient.moveDirectory();

        Writer writer = new Writer("Text.tmp");
        writer.fileConnect();
        writer.write();

        Reader reader = new Reader("text.tmp");
        reader.fileConnect();
        reader.fileRead();

        reader.fileDisconnect();
        writer.fileDisconnect();
        ftpClient.disConnect();

    }
}

현재 클라이언트가 다 의존성을 가지고 만들고있는데, 이부분을 facade객체를 만들어서 이 인터페이스를 활용!

-> SftpClient 라는 객체 만든다

public class SftpClient {

    private Ftp ftp;
    private Reader reader;
    private Writer writer;

    public SftpClient(Ftp ftp, Reader reader, Writer writer){
        this.ftp = ftp;
        this.reader = reader;
        this.writer = writer;

    //오버로딩
    public SftpClient(String host, int port, String path, String fileName){
        this.ftp = new Ftp(host, port, path);
        this.reader = new Reader(fileName);
        this.writer = new Writer(fileName);
    }

    public void connect(){
        this.ftpProtocol.connect();
        this.ftpProtocol.moveDirectory();
        this.fileReader.fileConnect();
        this.fileWriter.fileConnect();

    }

    public void write(String content){
        this.fileWriter.fileWrite(content);
    }

    public String read(){
        return this.fileReader.fileRead();
    }

    public void disConnect(){
        this.fileReader.fileDisconnect();
        this.fileWriter.fileDisconnect();
        this.ftpProtocol.disConnect();
    }

}

 

-> main 코드가 이렇게 변함

SftpClient sftpClient = new SftpClient("www.foo,co.kr", 22, "/home/etc", "text.tmp");
        sftpClient.connect();

        sftpClient.write();
        sftpClient.rear();
        sftpClient.disconnect();

 

 

전략(Strategy)패턴

- 유사한 행위들을 캡슐화하여 객체의 행위를 바꾸고 싶은 경우 직접 변경하는것이 아닌 전략만 변경하여 유연하게 확장하는 패턴

- SOLID중에서 개방폐쇄원칙(OCP)과 의존역전원칙(DIP) 따름

- 전략에 따라 결과가 달라짐!!

 

예제 ) 인코딩관련 - 아래 3가지가 다 있어야 함

1) 전략메서드를 가진 전략객체 (Normal Strategy, Base64 strategy)

2) 전략객체를 사용하는 컨텍스트 (Encoder)

3) 전략객체를 생성해 컨텍스트에 주입하는 클라이언트 -> 메인 메소드에 구현

메인메소드

    Encoder encoder = new Encoder();

    //base64의 전략
    EncodingStrategy base64 = new Base64Strategy();
    //normal의 전략
    EncodingStrategy normal = new NormalStrategy();

    //전략 집어넣기
    String message = "hello java";

    encoder.setEncodingStrategy(base64);
    String base64Result = encoder.getMessage(message);
    System.out.println(base64lResult);


    encoder.setEncodingStrategy(normal);
    String normalResult = encoder.getMessage(message);
    System.out.println(normalResult);

    encoder.setEncodingStrategy(new AppendStrategy());
    String appendResult = encoder.getMessage(message);
    System.out.println(appendResult); //출력 : ABCDhello java
public class NormalStrategy implements EncodingStrategy {
    @Override
    public String encode(String text) {
        return text;
    }
}
public class Base64Strategy implements EncodingStrategy{
    @Override
    public String encode(String text) {
        return Base64.getEncoder().encodeToString(text.getBytes());
    }
}
public class AppendStrategy implements EncodingStrategy{
    @Override
    public String encode(String text) {
        return "ABCD" + text;
    }
}
public class Encoder {
    //그때그때마다 전략을 주입받음
    private EncodingStrategy encodingStrategy;

    //인코더 세팅을 위함
    public String getMessage(String message){ //각각의 전략에 따라 결과가 달라짐
        return encodingStrategy.encode(message);
    }

    public void setEncodingStrategy(EncodingStrategy encodingStrategy){
        this.encodingStrategy = encodingStrategy;
    }
}
public interface EncodingStrategy {
    String encode(String text); //텍스트를 받아서 encode시켜줌
}