여러 개의 객체와 실제 사용하는 서브 객체의 사이에 복잡한 의존관계가 있을 때, 중간에 facade라는 객체를 두고, 여기서 제공하는 interface만을 활용하여 기능을 사용하는 방식이다.
Facade는 자신이 가지고 있는 각 클래스의 기능을 명확히 알아야 한다.
package com.company.design.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 : " + host + " Port : " + port + " 로 연결 합니다.");
}
public void moveDirectory(){
System.out.println("FTP path : " + path + "로 이동 합니다.");
}
public void disConnect(){
System.out.println("FTP 연결을 종료 합니다.");
}
}
package com.company.design.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);
}
}
package com.company.design.facade;
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 fileDisConnect(){
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);
}
}
package com.company.design.facade;
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(){
ftp.connect();
ftp.moveDirectory();
writer.fileConnect();
reader.fileConnect();
}
public void disConnect(){
writer.fileDisConnect();
reader.fileDisconnect();
ftp.disConnect();
}
public void read(){
reader.fileRead();
}
public void write(){
writer.write();
}
}
package com.company.design.facade;
public class main {
public static void main(String[] args) {
// 평범한 로직 이용
// Ftp ftpClient = new Ftp("www.step1.com", 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();
// 퍼사드 패턴사용
SftpClient sftpClient = new SftpClient("www.step1.com", 22, "/home/etc", "text.tmp");
sftpClient.connect();
sftpClient.write();
sftpClient.read();
sftpClient.disConnect();
}
}
데코레이터 패턴은 기존 뼈대(클래스)는 유지하되, 이후 필요한 형태로 꾸밀 때 사용한다. 확장이 필요한 경우 상속의 대안으로도 활용 한다. SOLID중에서 개방폐쇄 원칙(OCP)과 의존 역전 원칙(DIP)를 따른다.
package com.company.design.decorator;
public interface ICar {
int getPrice();
void showPrice();
}
package com.company.design.decorator;
public class Audi implements ICar{
private int price;
public Audi(int price){
this.price = price;
}
@Override
public int getPrice() {
return price;
}
@Override
public void showPrice() {
System.out.println("audi의 가격은 " + this.price + "원 입니다.");
}
}
package com.company.design.decorator;
public class AudiDecorator implements ICar{
protected ICar audi;
protected String modelName;
protected int modelPrice;
public AudiDecorator(ICar audi, String modelName, int modelPrice){
this.audi = audi;
this.modelName = modelName;
this.modelPrice = modelPrice;
}
@Override
public int getPrice() {
return audi.getPrice() + modelPrice;
}
@Override
public void showPrice() {
System.out.println(modelName + "의 가격은 " + getPrice() + " 원 입니다.");
}
}
package com.company.design.decorator;
public class A3 extends AudiDecorator{
public A3(ICar audi, String modelName) {
super(audi, modelName, 1000);
}
}
package com.company.design.decorator;
public class A4 extends AudiDecorator{
public A4(ICar audi, String modelName) {
super(audi, modelName, 2000);
}
}
package com.company.design.decorator;
public class A5 extends AudiDecorator{
public A5(ICar audi, String modelName) {
super(audi, modelName, 3000);
}
}
package com.company.design.decorator;
public class main {
public static void main(String[] args) {
ICar audi = new Audi(1000);
audi.showPrice();
// a3
ICar audi3 = new A3(audi, "A3");
audi3.showPrice();
// a4
ICar audi4 = new A4(audi, "A4");
audi4.showPrice();
// a5
ICar audi5 = new A5(audi, "A4");
audi5.showPrice();
}
}
Proxy Class를 통해서 대신 전달하는 형태로 설계되며, 실제 Client는 Proxy로 부터 결과를 받는다.
Cache의 기능으로도 활용이 가능하다.
SOLID중에서 개방폐쇄 원칙(OCP)과 의존 역전 원칙(DIP)를 따른다.
package com.company.design.proxy;
public interface IBrowser {
HTML show();
}
package com.company.design.proxy;
public class HTML {
private String url;
public HTML(String url){
this.url = url;
}
}
package com.company.design.proxy;
public class Browser implements IBrowser{
private String url;
public Browser(String url){
this.url = url;
}
@Override
public HTML show() {
System.out.println("browser loading html from : " + url);
return new HTML(url);
}
}
package com.company.design.proxy;
public class BrowserProxy implements IBrowser{
private String url;
private HTML html;
public BrowserProxy(String url){
this.url = url;
}
@Override
public HTML show() {
if (html == null){
this.html = new HTML(url);
System.out.println("BrowserProxy loading html from : " + url);
}
System.out.println("BrowserProxy use cache html : " + url);
return html;
}
}
package com.company.design.aop;
import com.company.design.proxy.HTML;
import com.company.design.proxy.IBrowser;
public class AopBrowser implements IBrowser {
private String url;
private HTML html;
private Runnable before;
private Runnable after;
public AopBrowser(String url, Runnable before, Runnable after){
this.url = url;
this.before = before;
this.after = after;
}
@Override
public HTML show() {
before.run();
if (html == null){
this.html = new HTML(url);
System.out.println("AopBrowser html loading from : " + url);
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
after.run();
System.out.println("AopBrowser html cache : " + url);
return null;
}
}
package com.company.design;
import com.company.design.adapter.*;
import com.company.design.aop.AopBrowser;
import com.company.design.proxy.Browser;
import com.company.design.proxy.BrowserProxy;
import com.company.design.proxy.IBrowser;
import com.company.design.singleton.Aclazz;
import com.company.design.singleton.Bclazz;
import com.company.design.singleton.SocketClient;
import java.util.concurrent.atomic.AtomicLong;
public class Main {
public static void main(String[] args) {
IBrowser browser = new BrowserProxy("www.naver.com");
browser.show();
browser.show();
browser.show();
browser.show();
AtomicLong start = new AtomicLong();
AtomicLong end = new AtomicLong();
IBrowser aopBrowser = new AopBrowser("www.naver.com",
() -> {
System.out.println("before");
start.set(System.currentTimeMillis());
},
() -> {
long now = System.currentTimeMillis();
end.set(now - start.get());
}
);
aopBrowser.show();
System.out.println("loading time : " + end.get());
aopBrowser.show();
System.out.println("loading time : " + end.get());
}
}