SMALL

Observer Pattern

옵저버 패턴이란

객체의 상태 변화를 관찰하는 관찰자들, 즉 옵저버들의 목록을 객체에 등록하여
상태 변화가 있을 때마다 옵저버들에게 통지하도록 하는 디자인 패턴입니다.

 > 어떤 객체의 변경 사항이 발생하였을 때 이와 연관된 객체들에게 알려주는 디자인 패턴이다.

 

 

 

옵저버 패턴에는 주체 객체와 상태의 변경을 알아야 하는 관찰 객체(Observer Object)가 존재하며 

이들의 관계는 보통 1:N이다. ( 1:1도 가능 ) 

 

Observer Pattern 특징

  • Loose Coupling : 상호작용을 하긴 하지만, 서로에 대해 잘 모른다
                                객체 간의 의존성을 제거할 수 있다

 

객체 간의 의존성을 줄이기 위해서 interface를 정의한다 ( Subject, Observer ) 

public interface Subject {

	public void registerObserver(Observer o);

	public void removeObserver(Observer o);

	public void notifyObserver();
}

public interface Observer {

	public void update(float tmp, float humidity, float pressure);
	
}

public interface DisplayElement {
	public void display();
}

 

Subject의 구현체, Observer List를 가지고 있다

public class WeatherData implements Subject {

	private List<Observer> observers;
	private float temperature;
	private float humidity;
	private float pressure;

	public WeatherData() {
		observers = new ArrayList<Observer>();
	}

	@Override
	public void registerObserver(Observer o) {
		observers.add(o);
	}

	@Override
	public void removeObserver(Observer o) {
		int idx = observers.indexOf(o);

		if (idx >= 0) {
			observers.remove(idx);
		}
	}

	@Override
	public void notifyObserver() {
		for (Observer observer : observers) {
			observer.update(temperature, humidity, pressure);
		}
	}
	
	public void measurementsChanged() {
		notifyObserver();
	}
	
	public void setMeasurements(float temperature, float humidity, float pressure) {
		this.temperature = temperature;
		this.humidity = humidity;
		this.pressure = pressure;
		
		measurementsChanged();
	}

}

 

Observer의 구현체, subject를 가지고 있고, 그를 통해 자신을 등록한다 

public class CurrentConditionsDisplay implements Observer, DisplayElement {

	private float temperature;
	private float humidity;
	private WeatherData weatherData;

	public CurrentConditionsDisplay(WeatherData weatherData) {
		this.weatherData = weatherData;
		this.weatherData.registerObserver(this);
	}

	@Override
	public void display() {
		System.out.println("Current Condition : " + temperature + " F degrees , " 
        						+ humidity + " % humidity");

	}

	@Override
	public void update(float tmp, float humidity, float pressure) {
		this.temperature = tmp;
		this.humidity = humidity;

		display();
	}

}

 

수행 코드 

	public static void main(String[] args) {
		WeatherData weatherData = new WeatherData();
		
		CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
		
		weatherData.setMeasurements(80, 65, 30.4f);
		weatherData.setMeasurements(82, 70, 29.2f);
		weatherData.setMeasurements(78, 90, 28.4f);
	}

 

Java 개발자라면, java.util에서 제공하는 Observer 패턴 관련 코드를 이용할 수 있다.

다만, Subject에 해당하는 Observable은 인터페이스가 아니고 상속받아야 해서 사용하는데 제한이 있을 수 있다.

import java.util.Observable;
import java.util.Observer;

 

 

 

 

LIST
SMALL

Strategy Pattern

객체들이 할 수 있는 행위 각각에 대해 전략 클래스를 생성하고, 유사한 행위들을 캡슐화하는 인터페이스를 정의하여, 객체의 행위를 동적으로 바꾸고 싶은 경우 직접 행위를 수정하지 않고 전략을 바꿔주기만 함으로써 행위를 유연하게 확장하는 방법을 말합니다.

 

간단히 말해서 

객체가 할 수 있는 행위들 각각을 전략으로 만들어 놓고, 동적으로 행위의 수정이 필요한 경우 전략을 바꾸는 것만으로 행위의 수정이 가능하도록 만든 패턴입니다.

 

 

객체지향 원칙

  • 바뀌는 부분은 캡슐화한다
  • 상속보다는 구성을 활용한다
  • 구현이 아닌 인터페이스에 맞춰서 프로그래밍한다

전략 패턴에서는 위의 원칙들을 포함하는 구조가 가능하다.

 

public abstract class Duck {
	
	FlyBehavior flyBehavior;
	QuackBehavior quackBehavior;
	
	public Duck() {
		
	}
	
	public void performFly() {
		flyBehavior.fly();
	}
	
	public void performQuack() {
		quackBehavior.quack();
	}
	
	public abstract void display();
	
	public void swim() {
		System.out.println("s w i m ~ ~ ");
	}
	
	public void setFlyBehavior(FlyBehavior fb) {
		flyBehavior = fb;
	}
	
	public void setQuackBehavior(QuackBehavior qb) {
		quackBehavior = qb;
	}
	
}

Duck에서는 FlyBehavior, QuackBehavior에 해당하는 전략을 가지고 있다.

 

public interface FlyBehavior {
	public void fly();
}

public interface QuackBehavior {
	public void quack();
}

 

그리고 마지막으로, Duck과 전략들의 구현체들을 보자

public class FlyNoWay implements FlyBehavior {
	@Override
	public void fly() {
		System.out.println(" I cannot fly ~ ");
	}
}

public class FlyWithWings implements FlyBehavior {
	@Override
	public void fly() {
		System.out.println(" fly in the moon ~ ");
	}
}

public class Quack implements QuackBehavior {
	@Override
	public void quack() {
		System.out.println("Quack ~~ Quack ~");
	}
}

public class Squeak implements QuackBehavior{
	@Override
	public void quack() {
		System.out.println("Squeak ~~ squeak ~ ");
	}
}

 

public class MallardDuck extends Duck {

	public MallardDuck() {
		quackBehavior = new Quack();
		flyBehavior = new FlyWithWings();
	}
	
	@Override
	public void display() {
		System.out.println("I'm MallarDuck~~ ~");
	}

}
LIST
SMALL

퍼사드 패턴은 프로그래밍 언어에서 관심사를 분리하는 패턴이다.

퍼사드의 어원은 프랑스어로 건물의 정면이라는 뜻을 갖고 있다.

건물의 외관, 겉모습을 말하며 사전적으로는 '표면, 허울'로도 해석된다.

 

퍼사드는 요즘과 같이 협업과 대형 시스템을 개발하고 배포하는 데 자주 응용되는 패턴이다.

퍼사드는 시스템 결합과 사용이 용이하도록 관심사를 분리한다.

 

출처 : https://jobc.tistory.com/22

퍼사드 패턴은 강력한 결합 구조를 해결하기 위해 코드의 의존성을 줄이고 느슨한 결합으로 구조를 변경한다.

퍼사드 패턴은 메인 시스템과 서브 시스템 중간에 위치하는데, 새로운 인터페이스 계층을 추가하여 시스템 간 의존성을 해결한다.

 

퍼사드는 GoF에서 설명하는 구조 패턴 중 하나며 퍼사드 패턴은 싱글턴 추상 팩토리라고 불리기도 한다. 

 

퍼사드는 복잡한 구조의 서브 시스템을 간단하게 호출할 수 있도록 하는 인터페이스 모음이다.

먼저, 퍼사드를 사용하지 않는 경우에 Client 코드를 보자.

public class Package1 {

    public void process() {
        System.out.println("Package 1 Process  ");
    }
}

public class Package2 {

    public void process() {
        System.out.println("Package process _");
    }
}

public class Package3 {

    public void process() {
        System.out.println("Package 3 - process");
    }
}

위의 내용은 서브시스템을 간략하게 예제를 위해 만든 것 들이다.

 

그럼 직접적으로 호출하는 코드는 아래와 같다.

    public static void main(String[] args) {
        Package1 p1 = new Package1();
        Package2 p2 = new Package2();
        Package3 p3 = new Package3();

        p1.process();
        p2.process();
        p3.process();

    }

이러한 식으로 사용자 코드에 서브 시스템의 클래스를 직접적으로 가져다 사용하게 되어서 결합도가 높아진다.

 

그러면 퍼사드 패턴을 사용하는 경우를 보자.

public class Facade {

    private Package1 p1;
    private Package2 p2;
    private Package3 p3;

    Facade() {
        p1 = new Package1();
        p2 = new Package2();
        p3 = new Package3();
    }

    public void processFacade() {
        p1.process();
        p2.process();
        p3.process();
    }
}
    public static void main(String[] args) {
        Facade facade = new Facade();
        facade.processFacade();

    }

각각의 패키지 객체를 직접 실행하는 것이 아니라 퍼사드 패턴의 메서드를 통해 한번에 실행한다.

 

퍼사드 패턴의 효과

  • 서브 시스템 보호
    • 클라이언트가 서브 시스템의 구성 요소를 직접 호출하지 않으므로 잘못된 사용을 방지할 수 있다
  • 확장성
  • 결합도 감소
    • 직접적으로 서브 시스템의 객체에 접근하지 않고 인터페이스와 유사한 역할을 하는 퍼사드를 이용하여 서브 시스템에 접근
  • 계층화
    • 서브 시스템이 계층화된 구조를 갖더라도 퍼사드는 계층 단계별로 접근하여 행위를 호출할 수 있다
  • 이식성
  • 공개 인터페이스
    • 퍼사드는 공개되는 기능만 인터페이스로 제공하는데, 이 경우 일시적으로 특정 기능을 감추는 효과를 얻을 수 있다

 

LIST
SMALL

장식자 패턴은 객체에 동적 기능을 추가하기 위해 구조를 개선하는 패턴이다.

다양한 확장을 위해 객체를 조합한다.

 

장식자 패턴은 기존 객체를 확장하기 위해 무언가를 추가 장식합니다.

장식자는 기본 베이스의 객체를 시작점으로 장식을 추가해 객체를 확장합니다.

장식자 패턴은 동적으로 객체를 결합하기 위해서 객체지향의 구성을 통해 확장한다.

 

장식자 패턴은 4개의 구성 요소로 이루어져 있습니다.

  • Component : 인터페이스를 정의
  • ConcreteComponent : 인터페이스에 정의 실제를 구현
  • Decorator : 컴포넌트를 참조하여 인터페이스를 일치화
  • ConcreteDecorator : 확장 및 추가되는 기능을 작성

 

Component - 공통 기능을 정의 

public interface Component {
    public String product();

    public int price();
}

 

ConcreteComponent

public class Product1 implements Component {

    @Override
    public String product() {

        return "one pice";
    }

    @Override
    public int price() {
        return 20000;
    }

}

 

Decorator 

장식자 패턴을 적용하기 위해 컴포넌트와 동일한 인터페이스를 유지해야 한다

public abstract class Decorate implements Component {
    abstract public String product();

    abstract public int price();
}

장식자 추상 클래스를 선언할 때 컴포넌트 인터페이스를 같이 적용

장식자는 객체를 확장할 때 구성을 사용한다.

인터페이스를 적용하는 이유는 단지 객체의 통일화된 사용을 위해서이다.

장식자는 실제 장식을 구현하기 위한 껍데기일 뿐이다.

 

ConcreteDecorator

public class CpuI7 extends Decorate {

    public Component base;

    CpuI7(Component concrete) {
        this.base = concrete;
    }

    @Override
    public String product() {
        return base.product() + ", i7";
    }

    @Override
    public int price() {
        return base.price() + 475000;
    }

}
public class Ssd256 extends Decorate {

    public Component base;

    Ssd256(Component concrete) {
        this.base = concrete;
    }

    @Override
    public String product() {
        return base.product() + ", ssd256";
    }

    @Override
    public int price() {
        return base.price() + 11000;
    }

}

 

실행 예시

    public static void main(String[] args) {
        Component p1 = new Product1(); // ConcreteComponent

        Component i7 = new CpuI7(p1); // ConcreteDecorator

        Component ssd256 = new Ssd256(i7); // ConcreteDecorator

        System.out.println("product : " + ssd256.product());
        System.out.println("price : " + ssd256.price());
    }
    
    //
    product : one pice, i7, ssd256
    price : 506000
LIST

+ Recent posts