Part3 - CH2. 디자인패턴(2) 퍼사드, 전략 패턴
Facade Pattern
Facade는 건물의 앞쪽 정명 이라는 뜻을 가진다. 여러 객체와 실제 사용하는 서브 객체의 사이에 복잡한 의존 관계가 있을 때, 중간에 facade라는 객체를 두고 여기서 제공하는 interface만을 활용하여 기능을 사용하는 방식이다. Facade는 자신이 가지고 있는 각 클래스의 기능을 명확히 알아야 한다.
FTP클라이언트를 만든다고 가정하고 코드를 구현해 보자. 일단 Facade패턴을 적용하지 않은 코드를 먼저 구현해 보자.
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 + "Port : " + port + "로 연결합니다.");
}
public void moveDirectory(){
System.out.println("FTP Path : " + path + "로 이동합니다.");
}
public void disConnect(){
System.out.println("FTP 연결을 종료합니다.");
}
}
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 fileWrite(){
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);
}
}
//psvm
Ftp ftpClient = new Ftp("www.foo.co.kr", 22, "/home/etc");
ftpClient.connect();
ftpClient.moveDirectory();
Writer writer = new Writer("text.tmp");
writer.fileConnect();
wrtier.fileWrite();
Reader reader = new Reader("text.tmp");
reader.fileConnect();
reader.fileRead();
reader.fileDisconnect();
writer.fileDisconnect();
ftpClient.disConnect();
지금은 각각 ftpClient, reader, writer 객체를 따로 만들어서 사용하고 있다. Facade객체를 사용하면 어떻게 되는지 알아보자.
public class SftpClient{
private Ftp;
private Reader;
private 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(){
ftp.connect();
ftp.moveDirectory();
writer.fileConnect();
reader.fileConnect();
}
public void disConnect(){
writer.fileDiscconect();
reader.fileDisconnect();
ftp.disconnect();
}
public void read(){
reader.fileRead();
}
public void write(){
writer.fileWrite();
}
}
위와 같이 Facade객체에서 main에서 수행했어야할 객체와의 의존성을 전부 가져가고, main함수에서는 이 Sf tpClient와의 의존성만 가지고 있으면 되도록 하였다. 그럼 main함수를 다시 작성해보자.
//psvm
SftpClient sftpClient = new SftpClient("www.foo.co.kr", 22, "/home/etc", "text.tmp");
sftpClient.connect();
sftpClient.write();
sftpClient.read();
sftpClient.disconnect();
즉 Facade패턴은 여러가지 객체와의 의존성을 한 객체안에 숨겨서 외부에서 사용할때 코드의 복잡도를 낮추고 더 편하게 쓸 수 있도록 해준다.
Strategy Pattern
전략 패턴이라고 불리며, 객체지향의 꽃이다. 유사한 행위들을 캡슐화하여, 객체의 행위를 바꾸고 싶은 경우 직접 변경하는 것이 아니라 전략만 변경하여, 유 연하게 확장하는 패턴이다. SOLID원칙 중 개방폐쇄원칙(OCP)와 의존역전원칙(DIP)를 따른다.
Encoder를 구현한다고 가정하고 코드를 구현해 볼 것이다.
전략패턴을 구현하기 위해 필요한 구성요소는 다음과 같다.
- 전략 메소드를 가진 전략 객체 (Normal Strategy, Base64 Strategy)
- 전략 객체를 사용하는 컨텍스트 (Encoder)
- 전략 객체를 생성해 컨텍스트에 주입하는 클라이언트
public interface EncodingStrategy{
String encodes(String text);
}
public class NormalStrategy implements EncodingStrategy{
@Override
public String encodes(String text){
return text;
};
}
public class Base64Strategy implements EncodingStrategy{
@Override
public String encodes(String text){
return Base64.getEncoder().encodeToString(text.getBytes());
};
}
public class Encoder{
private EncodingStrategy encodingStrategy;
public void getMessage(String message){
return this.encodingStrategy.encode(message);
}
public void setEncodingStrategy(EncodingStrategy encodingStrategy){
this.encodingStrategy = encodingStrategy;
}
}
//psvm
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(base64result);
encoder.setEncodingStrategy(normal);
String normalResult = encoder.getMessage(message);
System.out.println(normalResult);
여기까지 구현한 시점에서, 전략패턴의 확장용이성을 보기 위해 전략을 하나 더 추가해보자.
public class AppendStrategy implements EncodingStrategy{
@Override
public String encodes(String text){
return "ABCD" + text;
}
}
그러면 여기서 메인에다가 하기 코드만 추가하면 된다.
encoder.setEncodingStrategy(new AppendStrategy());
String appendResult = encoder.getMessage(message);
패스트캠퍼스 올인원 강의에서 제공하는 디자인 패턴을 모두 알아 보았는데, 디자인패턴에 더욱 관심이 많이 생겨 서, CH4-1포스트에 나와있는 33종의 디자인 패턴을 한번 쭉 정리해보는 시간을 가져봐야겠다는 생각이 들었다.