Java - 16. 디자인 패턴

갓김치·2020년 10월 19일
0

고급자바

목록 보기
39/47

객체 지향 설계 SOLID

객체 지향 설계 SOLID

S: Single responsibility principle(SRP)

  • 단일 책임 원칙
  • 한 클래스는 하나의 책임만 가져야 한다

O: Open/closed principle(OCP)

  • 개방-폐쇄 원칙
  • 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.

L: Liskov substitution principle(LSP)

  • 리스코프 치환 원칙
  • 프로그램의 객체는 프로그램의 정확성은 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.

I: Interface segregation principle(ISP)

  • 인터페이스 분리 원칙
  • 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나 보다 낫다

D: Dependency inversion priciple

  • 의존관계 역전 원칙
  • 프로그래머는 "추상화에 의존해야지, 구체화에 의존하면 안된다.

디자인 패턴

  • 숙련된 객체지향 소프트웨어 개발자에 의해 사용되는 베스트 사례 모음집
  • 소프트웨어 개발 중 직면하게 되는 일반적인 문제들의 해결을 위한 솔루션 모음집.
  • 오랜 기간 동안 수많은 개발자들에 의해 시도되고 테스트된 솔루션 모음집
  • 참고도서: GoF의 디자인패턴: 재사용성을 지닌 객체지향 소프트웨어의 핵심요소

Creational Pattern 생성 패턴

  • 객체 생성 방법을 추상화 하기 위한 패턴
  • 상황에 맞는 적절한 객체 생성으로 효율성 제고

1. Abstract Factory 추상 팩토리

  • Tutorialspoint - Abstract Factory
  • 구체적인 클래스를 지정하지 않고 관련성을 갖는 객체들의 집합을 생성하거나 서로 독립적인 객체들의 집합을 생성할 수 있는 인터페이스를 제공하는 패턴
  • 팩토리(객체생성하는 역할)긴 팩토린데 무슨 팩토린지 모른다 = 추상적
  • 팩토리를 위한 팩토리

2. ⭐ Builder 빌더

  • Tutorialspoint - Builder
  • A Builder class builds the final object step by step
  • This builder is independent of other objects.
  • 복합 객체의 생성 과정과 표현 방법을 분리하여 동일한 생성 절차에서 서로 다른 표현 결과를 만들 수 있게 하는 패턴
  • 인자가 많은 생성자정적 팩토리(객체 생성 메서드가 정적 메서드)가 필요한 클래스를 설계할 때 사용
    • 특히, 대부분의 인자값이 선택적인 상황에 유용함
  • 장점
    • 점층적 생성자 패턴보다 가독성이 좋아진다
    • 빌더로 생성된 객체 (Immutable)는 자바빈 객체(Mutable)를 사용할 때 보다 안전해 진다.
  • 특징
    • ★ 객체 생성시에만 정보를 저장할 수 있다.
    • ★ 값을 변경할 수 있는 메서드는 따로 제공되지 않는다
      • 1) immutable 객체
      • 2) read만 가능하기 때문에 동기화 필요 x
      • 3) thread 안전한 멀티스레드 프로그래밍 가능

점층적 생성자 패턴, 자바빈, 빌더

  • 점층적 vs. 자바빈 vs. 빌더
    • 점층적 생성자 패턴
      • Telescoping Constructor Pattern
      • `필수 매개변수만 받는 생성자
      • 필수 매개변수 + 선택 매개변수 1개 생성자
      • 필수 매개변수 + 선택 매개변수 2개 생성자
      • 매개변수가 많아지면 가독성이 매우 떨어진다
    • 자바빈즈 패턴
      • JavaBeans Pattern
      • 매개변수가 없는 생성자로 객체를 만든후, setter를 호출 해 값 설정
      • 장점: 점층적 생성자 패턴에 비해 가독성 상승
      • 단점: 매개변수에 따라 setter 여러개 호출 -> 1회의 호출로 객체 생성이 끝나지 않기 때문에 객체의 일관성이 깨짐
      • 일관성이 깨지기때문에 클래스를 불변으로 만들 수 없으며, 스레드 안전성을 위해 프로그래머가 추가 작업을 해줘야 함

예제

Member.java

public class Member {

  // 필수값
  private String name;
  private int age;

  // 선택값
  private String tel;
  private String address;
  private String hobby;
  private String birthDate;

  // 필수값 생성자
  private Member(String name, int age) {
    this.name = name;
    this.age = age;
  }

  // 생성자를 private으로. (Builder에서는 호출 가능)
  private Member(Builder builder) {
    this.name = builder.name;
    this.age = builder.age;
    this.tel = builder.tel;
    this.address = builder.address;
    this.hobby = builder.hobby;
    this.birthDate = builder.birthDate;
  }

  // 내부 Builder 클래스
  public static class Builder {
    // 필수값
    private String name;
    private int age;

    // 선택값
    private String tel;
    private String address;
    private String hobby;
    private String birthDate;

    // Builder 생성자
    public Builder(String name, int age) {
      this.name = name;
      this.age = age;
    }

    // 선택값 초기화를 위한 메서드들
    public Builder tel(String tel) {
      this.tel = tel;
      return this;
    }

    public Builder address(String address) {
      this.address = address;
      return this;
    }

    public Builder hobby(String builder) {
      this.hobby = hobby;
      return this;
    }

    public Builder birthDate(String birthDate) {
      this.birthDate = birthDate;
      return this;
    }

    // Builder객체를 이용하여 Member객체 생성하기
    public Member build() {
      return new Member(this);
    }
  } // class Builder
} // class Member

BuilderPatternDemo.java

public class BuilderPatternDemo {
  public static void main(String[] args) {
  
  // ★ 객체 생성시에만 정보를 저장할 수 있다.
  // ★ 값을 변경할 수 있는 메서드는 따로 제공되지 않는다
  // ==> 1. immutable 객체
  // ==> 2. read만 가능하기 때문에 동기화 필요 x
  // ==> 3. thread 안전한 멀티스레드 프로그래밍 가능
  
  Member member = new Member
  		    .Builder("홍길동", 18) /*필수값*/
                    .birthDate("2000-03-22") /*메서드체이닝*/
                    .hobby("독서")
                    .build();
                    // 코드 가독성 ↑
  }
}

3. ⭐ (Simple) Factory 팩토리

  • Tutrialspoint - Factory
  • 객체생성 방법을 클라이언트에게 노출시키지 않고(캡슐화), 공통 인터페이스를 통해 인스턴스를 생성하도록 하는 패턴
  • 변경될 수 있는 변수를 줄여 유지보수가 용이

예제

인터페이스 Shape

public interface Shape {
  void draw();
}

구현체 Rectangle, Square, Circle

public class Rectangle implements Shape {
  @Override
  public void draw() {
    System.out.println("직사각형");
  }
}

public class Square implements Shape {
  @Override
  public void draw() {
    System.out.println("정사각형");
  }
}

public class Circle implements Shape {
  @Override
  public void draw() {
    System.out.println("원");
  }
}

도형 객체 팩토리 ShapeFactory.java

public class ShapeFactory {
  public Shape getShape(String shapeType) {
    if(shapeType == null) {
      return null;
    }

    if(shapeType.equalsIgnoreCase("CIRCLE")) {
      return new Circle();
    }
    if(shapeType.equalsIgnoreCase("RECTANGLE")) {
      return new Rectangle();
    }
    if(shapeType.equalsIgnoreCase("SQUARE")) {
      return new Square();
    }
  }
}

FactoryPatternDemo.java

public class FactoryPatternDemo {
  public static void main(String[] args) {
    ShapeFactory factory = new ShapeFactory();

    Shape shape1 = factory.getShape("CIRCLE");
    shape1.draw();
    Shape shape2 = factory.getShape("RECTANGLE");
    shape2.draw();
    Shape shape3 = factory.getShape("SQUARE");
    shape3.draw();
  }
}

4. Factory Method 팩토리 메서드

  • Tutorialspoint - Factory Method
  • 객체를 생성하는 인터페이스는 미리 정의하되, 인스턴스를 만들 클래스의 결정은 서브클래스 쪽에서 내리는 패턴

5. Prototype 원형

  • Tutorialspoint - Prototype Pattern
  • 생성할 객체의 종류를 명세하는 데에 원형이 되는 예시물을 이용하고, 그 원형을 복사함으로써 새로운 객체를 생성하는 패턴
  • clone()

6. Singleton 단일체

  • 어떤 클래스의 인스턴스는 오직 하나임을 보장하며, 이 인스턴스에 접근할 수 있는 전역적인 접촉점을 제공하는 패턴

구조 패턴 Structural

더 큰 구조 또는 새로운 기능을 형성하기 위해 어떻게 클래스 및 객체를 합성하는가와 관련된 패턴

1. ⭐ Adapter 적응자

2. Bridge 가교

  • Tutorialspoint - Bridge
  • 구현부에서 추상층을 분리하여 각자 독립적으로 변형할 수 있게 하는 패턴
  • 잘 안나옴

3. Composite 복합체

  • Tutorialspoint - Composite
  • 객체들의 관계를 트리 구조로 구성하여 부분-전체 계층을 표현하는 패턴
  • 메뉴 구성

4. ⭐ Decorator 장식자

  • Tutorialspoint - Decorator
  • 주어진 상황에서 용도에 따라 어떤 객체에 책임을 덧붙이는 패턴
  • 보조스트림처럼 기존 기능에 새로운 기능을 덧붙이는 역할
    • 기존소스는 건들지 않는다

예제

인터페이스 Shape

public interface Shape {
  void draw();
}

구현체 Rectangle, Square, Circle

public class Rectangle implements Shape {
  @Override
  public void draw() {
    System.out.println("직사각형");
  }
}

public class Circle implements Shape {
  @Override
  public void draw() {
    System.out.println("원");
  }
}

추상 클래스 ShapeDecorator

public abstract class ShapeDecorator implements Shape {
  /*
   * abstract: 인터페이스 Shape의 draw()를 구현하지 않았기때문
   *  - 이 클래스를 가지고 객체를 안 만들겠다는 의미
   *  - 객체를 만드려면 '어떤 식으로 만들겠다'가 미리 정의되어야 하는데
   *  - abstract는 명시적으로 '나는 미완성 클래스'라고 컴파일러에게 알려주는 효과가 있기때문.
   */
  protected Shape decoratedShape;

  public ShapeDecorator(Shape decoratedShape) {
    this.decoratedShape = decoratedShape();
  }
}

빨간 경계선 클래스 RedShapeDecorator.java

public class RedShapeDecorator extends ShapeDecorator {
  public RedShapeDecorator(Shape decoratedShape) {
    super(decoratedShape); // default 생성자 아님 -> super 필수
  }

  @Override
  public void draw() {
    decoratedShape.draw(); // 원래 기능: (≒ 기반스트림)

    setRedBoarder(decoratedShape); // 새로운 기능
  }

  // 추가된 기능: 빨간 테두리
  private void setRedBorder(Shape decoratedShape) {
    System.out.println("빨간색 경계선 칠하기");
  }
}

DecoratorPatternDemo.java

public class DecoratorPatternDemo {
  public static void main(String[] args) {
    Shape circle = new Circle();

    Shape redCircle = new RedShapeDecorator(new Circle());

    Shape redRectangle = new RedShapeDecorator(new Rectangle());

    System.out.println("< 일반적인 원 그리기 >");
    circle.draw();
    System.out.println("----------------------------");

    System.out.println("< 빨간색 경계선 가진 원 그리기 >");
    redCircle.draw();
    System.out.println("----------------------------");

    System.out.println("< 빨간색 경계선 가진 직사각형 그리기 >");
    redRectangle.draw();
  }
}

결과

5. ⭐ Facade 퍼사드

  • Tutorialspoint - Facade
  • 서브시스템에 있는 인터페이스 집합에 대해서 하나의 통합된 인터페이스를 제공하는 패턴
  • 내부 작업내용은 감춘 채 겉으로는 간단한 인터페이스만 사용자에게 드러나도록
  • Facade pattern hides the complexities of the system and provides an interface to the client using which the client can access the system. This type of design pattern comes under structural pattern as this pattern adds an interface to existing system to hide its complexities.
    This pattern involves a single class which provides simplified methods required by client and delegates calls to methods of existing system classes.

예제

Create an interface.

public interface Shape {
	void draw();
}

Create concrete classes implementing the same interface.

public class Circle implements Shape{

	@Override
	public void draw() {
		System.out.println("Circle: draw()");
	}

}

public class Rectangle implements Shape{

	@Override
	public void draw() {
		System.out.println("Rectangle: draw()");
	}

}

public class Square implements Shape{

	@Override
	public void draw() {
		System.out.println("Square: draw()");
	}

}

Create a facade class.

public class ShapeMaker {
	private Shape circle;
	private Shape rectangle;
	private Shape square;
	
	public ShapeMaker() {
		circle = new Circle();
		rectangle = new Rectangle();
		square = new Square();
	}
	
	public void drawCircle() {
		circle.draw();
	}
	
	public void drawRectangle() {
		rectangle.draw();
	}
	
	public void drawSquare() {
		square.draw();
	}
}

Use the facade to draw various types of shapes.

public class FacadePatternDemo {
	public static void main(String[] args) {
		ShapeMaker shapeMaker = new ShapeMaker();
		
		shapeMaker.drawCircle();
		shapeMaker.drawRectangle();
		shapeMaker.drawSquare();
	}
}

Verify the output.

Circle: draw()
Rectangle: draw()
Square: draw()

6. Flyweight 플라이급

  • Tutorialspoint - Proxy
  • 크키가 작은 객체가 여러 개 있을 때, 공유를 통해 이들을 효율적으로 지원하는 패턴

7. ⭐ Proxy 프록시

  • Tutorialspoint - Proxy
  • 어떤 다른 객체로 접근하는 것을 통제하기 위해서 그 객체의 대리자(surrogate) 또는 자리채움자(placeholder)를 제공하는 패턴
  • 데코레이터와 비교
    • 권한, 검증 관점
      • 1) realImage가 있는지 체크
      • 1-1) 있다: 있는 realImage 반환
      • 1-2) 없다: 새로 읽어와서 반환
    • 기능 관점
      • 원래 있는 기능은 쓰되 그 기능에 +@
  • 예시
    • 중간에서 권한을 체크한 후 기능을 쓰게할 때

Behavior Pattern 행동 패턴

  • 어떤 처리의 책임을 어느 객체에 할당하는 것이 좋은지, 알고리즘을 어느 객체에 정의하는 것이 좋은지 등을 다루는 패턴
  • 객체 관점이기 때문에 런타임에 포커스가 많이 맞춰져있음

1. Chain of Responsibility 책임 연쇄

  • 요청을 처리할 수 있는 기회를 하나 이상의 객체에게 부여하여 요청을 보내는 객체와 그 요청을 받는 객체 사이의 결합을 피하는 패턴

2. ⭐ Command 명령

  • Tutorialspoint - Command
  • P18_CommandPatternExam
  • 요청을 객체의 형태로 캡슐화하여 서로 요청이 다른 사용자의 매개변수화.
  • 요청 저장 또는 로깅, 그리고 연산의 취소를 지원하게 만드는 패턴
  • 해당객체.execute() -> 객체의 execute()가 작동되면서 일함
    • 압축.execute() -> 압축해줌
    • 저장.execute() -> 저장해줌

3. Interpreter 해석자

  • 주어진 언어에 대해, 그 언어의 문법을 위한 표현 수단을 정의하고, 이와 아울러 그 표현 수단을 사용하여 해당 언어로 작성된 문장을 해석해난 해석기를 정의하는 패턴

4. ⭐ Iterator 반복자

  • Tutorialspoint - Iterator
  • 내부 표현부를 노출하지 않고 어떤 객체 집합에 속한 원소들을 순차적으로 접근할 수 있는 방법을 제공하는 패턴

5. Mediator 중재자

  • 한 집합에 속해있는 객체들의 상호작용을 캡슐화하는 객체를 정의하는 패턴
  • 넘어갈게요..

6. Memento 메멘토

  • 캡슐화를 위배하지 않은 채로 어떤 객체의 내부 상태를 잡아내고 실체화시켜, 이후에 해당 객체가 그 상태로 되돌아올 수 있도록 하는 패턴
  • 넘어갈게요.....

7. ⭐ Observer 감시자 - 이건 꼭 예제해볼것

  • Tutorialspoint - Observer
  • 객체들 사이에 일 대 다의 의존 관계를 정의해 두어 어떤 객체의 상태가 변할 때 그 객체에 의존성을 가진 다른 객체들이 그 변화를 통지 받고 자동으로 갱신될 수 있게 만드는 패턴
  • Observer 라는 abstract class: update()라는 메서드 가지고있음,
    • 이걸 extends해서 만든 Binary- Octal- Hexa- 이들은 update()를 구현하고 있다
  • 예시
    • 뉴스사이트
      • 새로운 뉴스가 갱신될 때마다 내가 알고싶어, 갱신될 때마다 나한테 notify해줘,
      • 내가 subject에 다가 attach()를 호출해서 나를 등록을 해놓으면? subject가 변화가 있을때마다 등록한 list에있는 observer객체들이 순회하면서 update들을 한다 (뭔소리여)
      • subject 1 : N observer
      • 서브젝트에 변화가잇을때마다 관련 Observer들의 update()기능이 호출이된다?
      • subject 변경시마다 update 실행 (= observer에게 notify)

8. State 상태

  • 객체의 내부 상태에 따라 스스로 행동을 변경할 수 있게끔 허가하는 패턴
  • 자주잘안나옴
  • command랑 약간 비슷

9. ⭐ Strategy 전략

  • Tutorialspoint - Strategy
  • headFirst 디자인 패턴 책보면 이거부터 나옴
  • 동일 계열의 알고리즘군을 정의하고, 각각의 알고리즘을 캡슐화하며, 이들을 상호교환이 가능하도록 만드는 패턴
  • 비즈니스로직을 바꿔서 써도 된다는..그런.. 패턴..... 요술봉같은..패턴...

예제

인터페이스 Strategy

public interface Strategy {
  public int doOperation(int num1, int num2);
}

구현체 Operation (+, -, *) 클래스들

public class OperationAdd implements Strategy{
  @Override
  public int doOperation(int num1, int num2) {
    return num1 + num2;
  }
}

public class OperationSubstract implements Strategy{
  @Override
  public int doOperation(int num1, int num2) {
    return num1 >= num2 ? num1 - num2 : num2 - num1;
  }
}

public class OperationMultiply implements Strategy{
  @Override
  public int doOperation(int num1, int num2) {
    return num1 * num2;
  }
}

구현체 Operation 내맘대로 클래스

public class OperationCustom implements Strategy{
  @Override
  public int doOperation(int num1, int num2) {
    return num1*num2+num2/num1-num2*num2;
  }
}

Context.java

  • 어떤 알고리즘을 쓸지는 Context객체 생성시점에 정해짐
public class Context {
  private Strategy strategy;

  public Context(Strategy strategy) {
    this.strategy = strategy;
  }

  public int exectue(int num1, int num2) {
    return strategy.doOperation(num1, num2);
  }
}

StrategyPatternDemo.java

public class StrategyPatternDemo {
  public static void main(String[] args) {
      Context context = new Context(new OperationAdd()); // Context객체 생성시 strategy를 매개변수로 넣어줌
    System.out.println("10 + 5 = " + context.execute(10, 5));

    context = new Context(new OperationSubstract());
    System.out.println("10 - 5 = " + context.execute(10, 5));

    context = new Context(new OperationMultiply());
    System.out.println("10 * 5 = " + context.execute(10, 5));

    context = new Context(new OperationCustom());
    System.out.println("10 내맘대로연산 20 = " + context.execute(10, 20));
  }
}

10. Template Method 템플릿 메서드

  • Tutorialspoint - Template
  • 객체의 연산에는 알고리즘의 뼈대만을 정의하고 각 단계에서 수행할 구체적 처리는 서브클래스 쪽으로 미루는 패턴
  • In Template pattern, an abstract class exposes defined way(s)/template(s) to execute its methods. Its subclasses can override the method implementation as per need but the invocation is to be in the same way as defined by an abstract class. This pattern comes under behavior pattern category.

예제

Create an abstract class with a template method being final.

public abstract class Game {
	abstract void initialize();
	abstract void startPlay();
	abstract void endPlay();
	
	// template method: defined way(s)/template(s) to execute its methods.
	public final void play() {
		
		// initialize the game
		initialize();
		
		// start game
		startPlay();
		
		// end game
		endPlay();
	}
}

Create concrete classes extending the above class.

public class Cricket extends Game{

	@Override
	void initialize() {
		System.out.println("Cricket Game Initialized! Start Playing.");
	}

	@Override
	void startPlay() {
		System.out.println("Cricket Game Started. Enjoy the game!");
	}

	@Override
	void endPlay() {
		System.out.println("Cricket Game Finished.");
	}

}
public class Football extends Game{

	@Override
	void initialize() {
		System.out.println("Football Game Initialized! Start Playing.");
	}

	@Override
	void startPlay() {
		System.out.println("Football Game Started. Enjoy the game!");
	}

	@Override
	void endPlay() {
		System.out.println("Football Game Finished.");
	}

}

Use the Game's template method play() to demonstrate a defined way of playing game.

public class TemplatePatternDemo {
	public static void main(String[] args) {
		
		Game game = new Cricket();
		game.play();
		System.out.println();
		Game game2 = new Football();
		game2.play();
	}
}

Verify the output.

Cricket Game Initialized! Start Playing.
Cricket Game Started. Enjoy the game!
Cricket Game Finished.

Football Game Initialized! Start Playing.
Football Game Started. Enjoy the game!
Football Game Finished.

11. Visitor 방문자

  • 객체 구조를 이루는 원소에 대해 수행할 연산을 표현하는 패턴
profile
갈 길이 멀다

0개의 댓글