자주 사용하는 설계 패턴을 정형화해서 이를 유형별로 가장 최적의 방법으로 개발할 수 있도록 정해둔 설계
객체지향 개념에 따른 설계 중 재사용할 경우 유용한 설계를 디자인 패턴으로 정리해둔 것이다.
Gof 디자인 패턴을 잘 이해하고 활용한다면 좋은 소프트웨어 설계가 가능하다.
객체를 생성하는 것과 관련된 패턴으로, 객체의 생성과 변경이 전체 시스템에 미치는 영향을 최소화하고 코드의 유연성을 높여준다.
Singleton
Factory Method
Prototype
Builder
Abstart Factory
Chaining
싱글톤 패턴 만드는 방법
- 클래스 내부에 private static 멤버변수 instance를 선언한다. data type은 반드시 해당 클래스와 같아야 한다.
- 클래스의 default constructor(기본생성자)를 private로 선언한다. 즉, 클래스 내부에서만 객체 생성이 가능하며 외부 클래스에서는 해당 클래스의 객체화가 불가능하게 만든다.
- 멤버변수 instance의 getter를 작성한다. 이로 인해 외부 클래스에서는 getter를 통해서만 class type 객체의 사용이 가능해진다.
package com.company.designpattern.singleton;
public class SocketClient {
private static SocketClient socketClient = null;
private SocketClient() {
}
public static SocketClient getInstance() {
if (socketClient == null) {
socketClient = new SocketClient();
}
return socketClient;
}
public void connect() {
System.out.println("connect");
}
}
package com.company.designpattern.singleton;
public class SocketA {
private SocketClient socketClient;
public SocketA() {
this.socketClient = SocketClient.getInstance();
}
public SocketClient getSocketClient() {
return this.socketClient;
}
}
package com.company.designpattern.singleton;
public class SocketB {
private SocketClient socketClient;
public SocketB() {
this.socketClient = SocketClient.getInstance();
}
public SocketClient getSocketClient() {
return this.socketClient;
}
}
프로그램 내의 자료구조나 인터페이스 구조 등 프로그램 구조를 설계하는 데 활용할 수 있는 패턴이다.
클래스, 객체들의 구성을 통해 더 큰 구조로의 확장을 용이하게 해준다.
서로 의존관계를 가지는 클래스 간의 복잡한 구조를 개발하기 쉽게, 유지보수하기 쉽게 만들어 준다.
Adapter
Decorator
Proxy
Facade
Composite
Flyweight
Bridge
package com.company.designpattern.adaptor;
public class SocketAdaptor implements Electronic110V {
private Electronic220V electronic220V;
public SocketAdaptor(Electronic220V electronic220V) {
this.electronic220V = electronic220V;
}
@Override
public void powerOn() {
electronic220V.connect();
}
}
package com.company.designpattern;
import com.company.designpattern.adaptor.Cleaner;
import com.company.designpattern.adaptor.Electronic110V;
import com.company.designpattern.adaptor.HairDryer;
import com.company.designpattern.adaptor.SocketAdaptor;
public class Main {
public static void main(String[] args) {
HairDryer hairDryer = new HairDryer();
connect(hairDryer);
Cleaner cleaner = new Cleaner();
Electronic110V adaptor = new SocketAdaptor(cleaner);
connect(adaptor);
}
// 콘센트
public static void connect(Electronic110V electronic110V) {
electronic110V.powerOn();
}
}
해당 객체와 타입이 다른 인터페이스를 구현하는 어댑터 객체를 통해
자기 자신의 형태는 변환시키지 않고 그 인터페이스의 속성을 활용할 수 있다.
아메리카노
컴포넌트
public abstract class Coffee {
public abstract void brewing();
}
public abstract class Decorator extends Coffee{
Coffee coffee;
public Decorator(Coffee coffee){
this.coffee = coffee;
}
@Override
public void brewing() {
coffee.brewing();
}
}
우유
데코레이터
public class Latte extends Decorator{
public Latte(Coffee coffee) {
super(coffee);
}
public void brewing() {
super.brewing();
System.out.print("Adding Milk ");
}
}
시럽
데코레이터
public class Mocha extends Decorator{
public Mocha(Coffee coffee) {
super(coffee);
}
public void brewing() {
super.brewing();
System.out.print("Adding Mocha Syrup ");
}
}
새로운 메뉴 추가
휘핑 크림
데코레이터
public class WhippedCream extends Decorator{
public WhippedCream(Coffee coffee) {
super(coffee);
}
public void brewing() {
super.brewing();
System.out.print("Adding WhippedCream ");
}
}
Browser 인터페이스는 html을 보여주는 역할을 하고, 구현체인 ChromeBrowser는 특정 url의 html 객체를 로딩한다.
package com.company.designpattern.proxy;
public interface Browser {
Html show();
}
package com.company.designpattern.proxy;
public class ChromeBrowser implements Browser{
private String url;
public ChromeBrowser(String url) {
this.url = url;
}
@Override
public Html show() {
System.out.println("ChromeBrowser loading html from : " + url);
return new Html(url);
}
}
여러번 같은 url을 요청받을 경우 이미 받아둔 결과를 돌려주는 ProxyBrowser를 만들어 보자.
package com.company.designpattern.proxy;
public class ProxyBrowser implements Browser{
private String url;
private Html html;
public ProxyBrowser(String url) {
this.url = url;
}
@Override
public Html show() {
if (html == null) {
this.html = new Html(url);
System.out.println("ProxyBrowser loading html from : " + url);
} else {
System.out.println("ProxyBrowser use cache html : " + url);
}
return html;
}
}
package com.company.design.facade;
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");
}
}
package com.company.design.facade;
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");
}
}
package com.company.design.facade;
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");
}
}
파일을 써서 보내고 읽는 작업을 실행한다고 할 때, 다음과 같은 과정으로 진행하게 된다.
1. FTP와 연결하고 디렉토리 이동한다.
2. FileWriter와 연결하고 파일을 쓴다.
3. FileReader와 연결하고 파일을 읽는다.
4. FTP, FileWriter, FileReader와 연결을 끊는다.
package com.company.design.facade;
public class SftpClient {
private FtpProtocol ftpProtocol;
private FileReader fileReader;
private FileWriter fileWriter;
public SftpClient(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();
}
}
반복적으로 사용되는 객체들의 상호작용을 패턴화한 것. 클래스나 객체들이 상호작용하는 방법과 책임을 분산하는 방법을 제공한다.
행위 패턴은 행위 관련 패턴을 사용하여 독립적으로 일을 처리하고자 할 때 사용한다.
Observer
Strategy
Template Method
Interpreter
Iterator
Visitor
Chain of responsibility
Command
Mediator
State
Memento
package com.company.design.observer;
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;
}
}
package com.company.design.observer;
public interface IButtonClickListener {
void clickEvent(String event);
}
public class Main {
public static void main(String[] args) {
Button button = new Button("버튼");
button.addListener(new IButtonListener(){
@Override
public void clickEvent(String event) {
System.out.println(event);
}
});
button.click("메시지 전달: click 1");
button.click("메시지 전달: click 2");
button.click("메시지 전달: click 3");
button.click("메시지 전달: click 4");
}
}