자주 사용하는 설계 패턴을 정형화 해서 이를 유형별로 가장 최적의 방법으로 개발을 할 수 있도록 정해둔 설계
객체를 생성하는 것과 관련된 패턴으로, 객체의 생성과 변경이 전체 시스템에 미치는 영향을 최소화 하고, 코드의 유연성을 높여 준다.
어떤한 클래스(객체)가 유일하게 1개만 존재 할 때 사용한다.
public class SocketClient {
private static SocketClient socketClient = null;
// default 생성자 막기
private SocketClient(){}
public static SocketClient getInstance(){
if(socketClient == null){
socketClient = new SocketClient();
System.out.println("socket new instance");
}
return socketClient;
}
}
클래스 내부의 SocketClient와 getInstance()는 static으로 선언한다.
getInstace() 메소드를 이용하여 객체가 null이면 새로 생성하고 null이 아니면 기존에 생성된 객체 return
프로그램 내의 자료구조나 인터페이스 구조 등 프로그램 구조를 설계하는데 활용 될 수 있는 패턴으로 클래스, 객체들의 구성을 통해서 더 큰 구조를 만들 수 있게 해준다.
한 클래스의 인터페이스를 클라이언트에서 사용하고자하는 다른 인터페이스로 변환한다.
public interface Electronic110V {
public void powerOn();
}
public interface Electronic220V {
public void powerOn();
}
public class HairDryer implements Electronic110V {
@Override
public void powerOn() {
System.out.println("110v 헤어드라이기 ON");
}
}
public class AirConditioner implements Electronic220V{
@Override
public void powerOn() {
System.out.println("220v 에어컨 ON");
}
}
public class SocketAdapter implements Electronic110V {
private Electronic220V electronic220V;
public SocketAdapter(Electronic220V electronic220V){
this.electronic220V = electronic220V;
}
@Override
public void powerOn() {
electronic220V.connect();
}
110V를 220V로 바꿔주는 Adaptor
public class Main {
public static void main(String[] args) throws InterruptedException {
HairDryer hairDryer = new HairDryer();
connect(hairDryer);
//"110v 헤어드라이기 ON" 출력
AirConditioner airConditioner = new AirConditioner();
connect(airConditioner);
//Error
Electronic110V _airConditioner = new SocketAdapter(airConditioner);
//110V로 바꿔주는 역할을 한다
connect(_airConditioner);
//"220v 에어컨 ON" 출력
}
public static void connect(Electronic110V electronic110V){
electronic110V.powerOn();
}
}
Proxy는 대리인 이라는 뜻으로, 뭔가를 대신해서 처리를 하는 것이다.
Proxy Class를 통햇서 대신 전달 하는 형태로 설계되며, 실제 Client는 Proxy로 부터 결과를 받는다.
Browser가 html을 보여줄 때 caching을 사용하는 것을 예시로 들겠다.
public class Html {
private String url;
public Html(String url){
this.url = url;
}
}
public interface IBroswer {
Html show();
}
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);
}
}
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);
}
//캐시가 있을 때
else{
System.out.println("BrowserProxy use cache html");
return html;
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
IBrowser Browser = new Browser("www.naver.com");
Browser.show();
Browser.show();
//결과
//"Browser loading html from www.naver.com"
//"Browser loading html from www.naver.com"
//새로 Html instance 생성
IBrowser Browser = new BrowserProxy("www.naver.com");
Browser.show();
Browser.show();
//결과
//"BrowserProxy loading html from www.naver.com"
//"BrowserProxy use cache html"
}
}
기존의 기능에 새로운 기능을 추가하고 싶을 경우, 새로운 기능을 Decorator로 만들어서 추가하는 방식이다.

Component : 실질적인 인스턴스를 컨트롤하는 역할
ConcreteComponent : Component의 실질적인 인스턴스의 부분으로 책임의 주체의 역할
Decorator : Component와 ConcreteDecorator를 동일시 하도록 해주는 역할
ConcreteDecoreator : 실질적인 장식 인스턴스 및 정의이며 추가된 책임의 주체
public interface Component {
String add()//재료 추가
}
public class BaseComponent implements Component {
@Override
public String add() {
return "라면";
}
}
abstract public class ComponentAddDecorator implements Component {
private Component ramenComponent;
public Decorator(Component ramenComponent) {
this.ramenComponent = ramenComponent;
}
public String add() {
return ramenComponent.add();
}
}
라면의 재료를 추가하는 Decorator
public class CheeseAddDecorator extends ComponentAddDecorator {
public CheeseDecorator(Component ramenComponent) {
super(ramenComponent);
}
@Override
public String add() {
return super.add() + " + 치즈";
}
}
public class TteokAddDecorator extends ComponentAddDecorator {
public TteokAddDecorator(Component ramenComponent) {
super(ramenComponent);
}
@Override
public String add() {
return super.add() + " + 떡";
}
}
public class Main {
public static void main(String[] args) {
Component ramen = new BaseComponent();
System.out.println("라면 : " + ramen.add());
//"라면 : 라면"
Component cheeseRamen = new CheeseAddDecorator(new BaseComponent());
System.out.println("치즈라면 : " + cheeseRamen.add());
//"치즈라면 : 라면 + 치즈"
Component tteokCheeseRamen = new TteokAddDecorator(new CheeseAddDecorator(new BaseComponent()));
System.out.println("떡치즈라면 : " + tteokRamen.add());
//"떡치즈라면 : 라면 + 치즈 + 떡"
}
}
Facade는 건물의 앞쪽 정면 이라는 뜻을 가지며, 여러 개의 객체와 실제 사용하는 서브 객체의 사이에 복잡한 의존관계가 있을 때, 중간에 facade라는 객체를 두고, 여기서 제공하는 interface만을 활용하여 기능을 사용하는 방식이다.
Sftp 프로토콜을 예시로 들어보겠다.
public class FtpProtocol {
public FtpProtocol(String host, int port, String path){
System.out.println("ftp server create");
}
public void connect(){
System.out.println("ftp server connected");
}
public void moveDirectory(){
System.out.println("move path");
}
public void disConnect(){
System.out.println("ftp server disConnected");
}
}
public class FileWriter {
public FileWriter(String fileName){
}
public void fileConnect(){
System.out.println("FileWriter Connected");
}
public void fileWrite(String content){
System.out.println("write : "+content);
}
public void fileDisconnect(){
System.out.println("FileWriter disConnected");
}
}
public class FileReader {
public FileReader(String fileName){
}
public void fileConnect(){
System.out.println("FileReader Connected");
}
public String fileRead(){
return "content";
}
public void fileDisconnect(){
System.out.println("FileReader disConnected");
}
}
SftpFacade Class
public class SftpFacade {
private FtpProtocol ftpProtocol;
private FileReader fileReader;
private FileWriter fileWriter;
public SftpFacade(String host, int port, String path, String fileName){
this.ftpProtocol = new FtpProtocol(host, port, path);
this.fileReader = new FileReader(fileName);
this.fileWriter = new FileWriter(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();
}
}
FtpProtocol, FileWriter, FileReader를 이용하여 facade를 구현하였다.
public class Main {
public static void main(String[] args) throws InterruptedException {
SftpFacade sftp=new SftpFacade("localhost",8000,"/test","test.txt")
sftp.connect()
sftp.write()
sftp.disconnect()
}
}
반복적으로 사용되는 객체들의 상호작용을 패턴화한 것으로, 클래스나 객체들이 상호작용하는 방법과 책임을 분산하는 방법을 제공한다.
객체의 상태 변화를 관찰하는 관찰자들, 즉 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴이다.
이벤트가 발생하면 각 Observer는 Subject의 notifyObservers()를 이벤트 발생 유무를 알 수 있다.
각각의 파생 Observer는는 notify 함수를 구현함으로써 이벤트가 발생했을 때 처리할 각자의 동작을 정의해야 한다.
Button Click Listener를 예시로 보이겠다.
public interface IButtonClickListener {
void clickEvent(String event);
}
Observer에 해당하며 clickEvent()는 notify()에 해당한다.
public class MyButton {
private String name;
private IButtonClickListener buttonClickListener;
public MyButton(String buttonName){
this.name = buttonName;
}
public void click(String clickEvent){
buttonClickListener.clickEvent(this.name+", "+clickEvent);
}
public void addListener(IButtonClickListener buttonClickListener){
this.buttonClickListener = buttonClickListener;
}
}
MyButton은 Subject에 해당하며 click()는 notifyObserver()에 해당한다.
위의 예시는 Subject에 Observer가 1개 뿐인 경우이다.
유사한 행위들을 캡슐화하여, 객체의 행위를 바꾸고 싶은 경우 직접 변경하는 것이 아닌 전략만 변경하여, 유연하게 확장하는 패턴이다.
Encoding을 예시로 들어보겠다.
public interface EncodingStrategy {
String encoding(String message);
}
public class NormalStrategy implements EncodingStrategy{
@Override
public String encoding(String message) {
return message;
}
}
public class Base64Strategy implements EncodingStrategy{
@Override
public String encoding(String message) {
return Base64.getEncoder().encodeToString(message.getBytes());
}
}
public class Encoder {
private EncodingStrategy encodingStrategy;
public String getMessage(String message){
return encodingStrategy.encoding(message);
}
public void setEncodingStrategy(EncodingStrategy encodingStrategy){
this.encodingStrategy = encodingStrategy;
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Encoder base64Encoder = new Encoder();
base64Encoder.setEncodingStrategy(new Base64Strategy());
System.out.println(base64Encoder.getMessage("message"));
Encoder normalEncoder = new Encoder();
normalEncoder.setEncodingStrategy(new NormalStrategy());
System.out.println(normalEncoder.getMessage("message"));
}
}