디자인 패턴 (Design Patterns)
- 디자인 패턴이 어떤 것인지와 대표적으로 사용하는 패턴에 대한 사용법 정도만 정리했다.
디자인 패턴이란
- 자주 발생하는 문제를 쉽게 해결하기 위해 제시된 재사용 가능한 해결책
- Don't reinvent the Wheel (바퀴를 다시 발명하지 마라)
- 이미 해결된 방식은 해결된 방식대로 해결하고, 새롭게 맞딱드린 문제를 해결하자
- 소프트웨어 설계 문제를 쉽게 해결할 수 있도록 패턴화된 설계 방식
- 팀원들과의 소통을 위해 디자인 패턴 학습이 필요하다.
- 팀원들이 싱글톤 패턴으로 해결 해주세요 할 때 이 패턴에 대해서 알고 있어야 하겠지?
디자인 패턴의 구조
- 문맥(Context)
- 문제(Problem)
- 패턴이 적용되어 해결되어야 하는 여러 설계 이슈를 기술
- 해결(Solution)
- 문제를 해결하는 설계 구성 요소와 구성 요소 사이의 관계를 기술
디자인 패턴의 종류
- Gang of four 패턴
- 생성 패턴 (Creational patterns)
- 객체의 생성 방식에 관련된 패턴
- Abstract Factory, Factory Method, Singleton ...
- 구조 패턴 (Structural patterns)
- 클래스/객체를 조합한 구조를 가지는 패턴
- Composite, Decorator ...
- 동작 패턴 (Behavioral patterns)
- 클래스/객체 사이의 동작 분배에 관련된 패턴
- Observer, State, Strategy, Template Method, Command ...
- 동시성 패턴 (Concurrency patterns)
- Scheduling, Monitor, Lock ...
- 아키텍처 패턴 (Architecture patterns)
- Model-View-Controller, Model-View-Presenter, Model-View-ViewModel ...
- 기타 패턴
- Dependency injection, Lazy loading, Mock object ...
대표적인 디자인 패턴
싱글톤 패턴
- 단 하나의 객체만 존재할 수 있는 클래스를 구현하는 패턴
class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
팩토리 패턴
- 구상 클래스 객체를 전담하여 생성하는 클래스를 구현하는 패턴
- 팩토리 메소드 패턴 (ref)
interface Shape {
void draw();
}
class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
class ShapeFactory {
Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
class FactoryPatternDemo {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
Shape shape1 = shapeFactory.getShape("CIRCLE");
shape1.draw();
Shape shape2 = shapeFactory.getShape("RECTANGLE");
shape2.draw();
Shape shape3 = shapeFactory.getShape("SQUARE");
shape3.draw();
}
}
추상 팩토리 패턴
public interface Shape {
void draw();
}
class RoundedRectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside RoundedRectangle::draw() method.");
}
}
class RoundedSquare implements Shape {
@Override
public void draw() {
System.out.println("Inside RoundedSquare::draw() method.");
}
}
class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
abstract class AbstractFactory {
abstract Shape getShape(String shapeType) ;
}
class ShapeFactory extends AbstractFactory {
@Override
public Shape getShape(String shapeType){
if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
}else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
class RoundedShapeFactory extends AbstractFactory {
@Override
public Shape getShape(String shapeType){
if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new RoundedRectangle();
}else if(shapeType.equalsIgnoreCase("SQUARE")){
return new RoundedSquare();
}
return null;
}
}
class FactoryProducer {
public static AbstractFactory getFactory(boolean rounded){
if(rounded){
return new RoundedShapeFactory();
}else{
return new ShapeFactory();
}
}
}
class AbstractFactoryPatternDemo {
public static void main(String[] args) {
AbstractFactory shapeFactory = FactoryProducer.getFactory(false);
Shape shape1 = shapeFactory.getShape("RECTANGLE");
shape1.draw();
Shape shape2 = shapeFactory.getShape("SQUARE");
shape2.draw();
AbstractFactory shapeFactory1 = FactoryProducer.getFactory(true);
Shape shape3 = shapeFactory1.getShape("RECTANGLE");
shape3.draw();
Shape shape4 = shapeFactory1.getShape("SQUARE");
shape4.draw();
}
}
데코레이터 패턴
- 생성자를 이용해 객체에 일정한 기능을 추가하는 패턴
interface Shape {
void draw();
}
class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Shape: Rectangle");
}
}
class Circle implements Shape {
@Override
public void draw() {
System.out.println("Shape: Circle");
}
}
abstract class ShapeDecorator implements Shape {
protected Shape decoratedShape;
public ShapeDecorator(Shape decoratedShape){
this.decoratedShape = decoratedShape;
}
public void draw(){
decoratedShape.draw();
}
}
class RedShapeDecorator extends ShapeDecorator {
public RedShapeDecorator(Shape decoratedShape) {
super(decoratedShape);
}
@Override
public void draw() {
decoratedShape.draw();
setRedBorder(decoratedShape);
}
private void setRedBorder(Shape decoratedShape){
System.out.println("Border Color: Red");
}
}
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 with normal border");
circle.draw();
System.out.println("\nCircle of red border");
redCircle.draw();
System.out.println("\nRectangle of red border");
redRectangle.draw();
}
}
옵저버 패턴
- Observable 객체의 변화를 Observer에서 알 수 있도록 하는 패턴
class Subject {
private List<Observer> observers = new ArrayList<Observer>();
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
notifyAllObservers();
}
public void attach(Observer observer){
observers.add(observer);
}
public void notifyAllObservers(){
for (Observer observer : observers) {
observer.update();
}
}
}
abstract class Observer {
protected Subject subject;
public abstract void update();
}
class BinaryObserver extends Observer{
public BinaryObserver(Subject subject){
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println( "Binary String: " + Integer.toBinaryString( subject.getState() ) );
}
}
class OctalObserver extends Observer{
public OctalObserver(Subject subject){
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println( "Octal String: " + Integer.toOctalString( subject.getState() ) );
}
}
class HexaObserver extends Observer{
public HexaObserver(Subject subject){
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println( "Hex String: " + Integer.toHexString( subject.getState() ).toUpperCase() );
}
}
class ObserverPatternDemo {
public static void main(String[] args) {
Subject subject = new Subject();
new HexaObserver(subject);
new OctalObserver(subject);
new BinaryObserver(subject);
System.out.println("First state change: 15");
subject.setState(15);
System.out.println("Second state change: 10");
subject.setState(10);
}
}