Mediator 패턴 그림

 

Mediator 패턴 개념

여러 객체 간의 통신을 관리하고, 각 객체가 서로 직접 참조하지 않고 중재자를 통해 통신할 수 있게 해주는 디자인 패턴. 통신 시스템에 적용하면, 모듈 간의 복잡한 상호작용을 단순화하고, 시스템의 결합도를 낮출 수 임.

 

Mediator 구성 요소

 

  • Mediator(중재자): 여러 객체 간의 통신을 중재하고 조율하는 객체.
  • Colleague(동료): Mediator를 통해 다른 객체와 상호작용하는 객체.

 

C++ 코드

Mediator 인터페이스 정의

class Colleague;

class Mediator {
public:
    virtual void sendMessage(const std::string &message, Colleague *colleague) = 0;
    virtual void registerColleague(Colleague *colleague) = 0;
};

 

 

Colleage 클래스 정의

class Colleague {
protected:
    Mediator *mediator;
public:
    Colleague(Mediator *m) : mediator(m) {}
    virtual void receiveMessage(const std::string &message) = 0;
};

 

Concrete Mediator 및 Concreate Colleague 구현

#include <vector>
#include <iostream>

class ConcreteMediator : public Mediator {
private:
    std::vector<Colleague*> colleagues;
public:
    void sendMessage(const std::string &message, Colleague *sender) override {
        for (auto colleague : colleagues) {
            if (colleague != sender) {
                colleague->receiveMessage(message);
            }
        }
    }

    void registerColleague(Colleague *colleague) override {
        colleagues.push_back(colleague);
    }
};

class ConcreteColleague : public Colleague {
public:
    ConcreteColleague(Mediator *m) : Colleague(m) {}

    void receiveMessage(const std::string &message) override {
        std::cout << "Colleague received: " << message << std::endl;
    }

    void send(const std::string &message) {
        mediator->sendMessage(message, this);
    }
};

 

사용 예제

int main() {
    ConcreteMediator mediator;

    ConcreteColleague colleague1(&mediator);
    ConcreteColleague colleague2(&mediator);

    mediator.registerColleague(&colleague1);
    mediator.registerColleague(&colleague2);

    colleague1.send("Hello from colleague 1");
    colleague2.send("Hello from colleague 2");

    return 0;
}

'Program > SW Design Patterns' 카테고리의 다른 글

CH8 스테이트 패턴  (0) 2023.12.04
CH7 어댑터&퍼사드 패턴  (0) 2023.03.19
CH6 커맨드 패턴  (0) 2023.03.07
CH5 데코레이터 패턴  (1) 2023.03.05
CH4 팩토리 패턴  (0) 2023.03.04

스테이트 패턴 정의

객체 내부에 구성되어있는 상태에 따라 객체의 행동을 바꾸는 패턴이다.  

 

필요한 상황

스테이트 패턴

- 로봇의 상태에 따라 수행하는 동작을 바꾸고 싶을 때

- GUI 프로그램에서 상태에 따라 버튼의 Enable, Disable 등 여부를 바꾸고 싶을 때

- 상태 변수를 switch로 넣어서 case로 때려 넣은 코드가 있을 때 (리펙토링 대상!!)

 

Class 다이어그램

스테이트 패턴

코드

스테이트 패턴

public interface State {
    void insertQuarter();
    void ejectQuarter();
    void turnCrank();
    void dispense();
}

public class HasQuarterSate : State {
    GumballMachine gumballMachine;
    public SoldOutState(GumballMachine gumballMachine)
    {
        this.gumballMachine = gumballMachine;
    }
    public void insertQuarter()=> Console.WriteLine("손잡이를 돌렸습니다.");
        
    public void ejectQuarter(){
        Console.WriteLine("동전이 반환됩니다.");
        gumballMachine.setState(gumballMachine.noQuarterState);
    }
    public void turnCrank() => Console.WriteLine("손잡이를 돌렸습니다.");
    public void dispense()=> Console.WriteLine("알맹이가 나갈수 없습니다.");
}

public class SoldState : State {
    GumballMachine gumballMachine;
    public SoldOutState(GumballMachine gumballMachine)
    {
        this.gumballMachine = gumballMachine;
    }
    public void insertQuarter()=> Console.WriteLine("잠깐만 기다려 주세요. 알맹이가 나가고 있습니다.");
    public void ejectQuarter()=> Console.WriteLine("이미 알맹이를 뽑으셨습니다.");
    public void turnCrank(){}=> Console.WriteLine("손잡이는 한 번만 돌려주세요.");
    public void dispense(){
        gumballMachine.releaseBall();
        if ( gumballMachine.count > 0){
            gumballMachine.setState(gumballMachine.noQuarterState);
        }
        else{
            Console.WriteLine("더이상 동전이 없습니다");
            gumballMachine.setState(gumballMachine.soldOutState);
        }
    }
}

public class SoldOutState : State {
    GumballMachine gumballMachine;
    public SoldOutState(GumballMachine gumballMachine)
    {
        this.gumballMachine = gumballMachine;
    }
    //인터페이스 구현체..
}

public class NoQuarterState : State {
    GumballMachine gumballMachine;
    public SoldOutState(GumballMachine gumballMachine)
    {
        this.gumballMachine = gumballMachine;
    }
    //인터페이스 구현체..
}

인터페이스와 구현체

 

public class GumballMachine {
    State soldOutState {get;set;}
    State noQuarterState{get;set;}
    State hasQuarterSate{get;set;}
    State soldState{get;set;}
    
    State state = soldOutState;
    int count {get;set;};
    
    public GumballMachine(int numberGumballs){
        soldOutState = new SoldOutState(this);
        noQuarterState = new NoQuarterState(this);
        hasQuarterSate = new HasQuarterSate(this);
        soldState = new SoldState(this);
        this.count = numberGumballs;
        if (numberGumballs > 0){
            state = noQuarterState;
        }
    }
    
    public void insertQuarter(){
        state.insertQuarter();
    }
    
    public void ejectQuarter(){
        state.ejectQuarter();
    }
    
    public void turnCrank(){
        state.turnCrank();
        state.dispense();
    }
    
    public void dispense(){
        state.dispense();
    }
    public void setState(State state){
        this.state = state;
    }
    
    public void releaseBall(){
        Console.WriteLine("A gumball comes rolling out the solt.....");
        if (count != 0){
            count = count - 1;
        }
    }
}

상태패턴을 이용하는 Context Class

코드 설명

스테이트 패턴

1. 인터페이스와 구현체

public interface State {
    void insertQuarter();
    void ejectQuarter();
    void turnCrank();
    void dispense();
}

Context Class에서 제공하는 기능을 인터페이스 함수로 대체했다.

 

public class HasQuarterSate : State {
    GumballMachine gumballMachine;
    public SoldOutState(GumballMachine gumballMachine)
    {
        this.gumballMachine = gumballMachine;
    }
    public void insertQuarter()=> Console.WriteLine("손잡이를 돌렸습니다.");
        
    public void ejectQuarter(){
        Console.WriteLine("동전이 반환됩니다.");
        gumballMachine.setState(gumballMachine.noQuarterState);
    }
    public void turnCrank() => Console.WriteLine("손잡이를 돌렸습니다.");
    public void dispense()=> Console.WriteLine("알맹이가 나갈수 없습니다.");
}

인터페이스의 구현체는 Context Class 인스턴스를 가지고 있으며, 인터페이스 구현체를 직접 실행하면서 상태 변경이 필요한 경우 Context Class 인스턴스의 상태를 직접 변경한다. 이부분에서 Context Class와의 종속성이 생긴다. (결합성이 올라감..)

 

2. 스테이트패턴을 이용하는 Context Class

Context Class는 스테이트 구현체들을 모두 가지고 있으며, 기능 실행이 필요할 때 인터페이스 구현체들의 함수만 호출한다.

설명 보충을 위한 그림 자료

스테이트 패턴

현재 상태 : HasQuarter, 상태별로 구현한 인터페이스 구현체들(슬롯 머신 기능 + 상태 천이 기능)

 

현재 상태 : Sold, 상태별로 구현한 인터페이스 구현체들(슬롯 머신 기능 + 상태 천이 기능)

필요에 따라 추가 설명

x

정리 및 결론

스테이트 패턴은 현재 프로그램의 상태에 따라 기능을 달리해야 할 때 적용할만한 패턴이다. 주요 특징은 아래와 같다.

1. 상태 인터페이스를 두고 그에 맞는 구현체들을 모든 상태 별로 구현한다.

2. 상태 인터페이스 구현체들은 온전히 자신의 기능만을 수행하며 상태 천이가 필요한 경우 Context Class의 상태 천이를 수행한다.

3. 전략 패턴과 유사하다. 

개인적으로 상태 패턴을 실제로 사용한 사례를 보고싶다. 전략 패턴, 옵저버 패턴, 팩토리 패턴, 데코 패턴은 많이 쓰인다고 들었는데 상태 패턴을 사용하는 사례를 보지 못했다. 

'Program > SW Design Patterns' 카테고리의 다른 글

Mediator 패턴 (C++)  (0) 2024.07.31
CH7 어댑터&퍼사드 패턴  (0) 2023.03.19
CH6 커맨드 패턴  (0) 2023.03.07
CH5 데코레이터 패턴  (1) 2023.03.05
CH4 팩토리 패턴  (0) 2023.03.04

어댑터 패턴 정의

한 클래스의 인터페이스를 다른 클래스의 인터페이스로변환시켜주는 기능을 제공하는 패턴

퍼사드 패턴 정의

여러 클래스의 인터페이스들을 하나의 통합 클래스로 통합한 패턴 

필요한 상황

어댑터 패턴

- 새로운 인터페이스를 이미 개발되어 있는 인터페이스로 변환시키고 싶을 때 (Array -> List 적용 등..)

퍼사드 패턴

- 나열되어 있는 여러 인터페이스를 통합하여 동시 실행 등 다양한 기능을 제공하고 싶을 때 (매크로 기능..)

Class 다이어그램

어댑터 패턴

퍼사드 패턴

코드

어댑터 패턴

서로 다른 인터페이스 코드

public interface VoltOf110
{
    void volt110On();
    void volt110Off();
}

public interface VoltOf220
{
    void volt220On();
    void volt220Off();
}

기존의 VoltOf110 인터페이스로 구현된 코드

public class DVDPlayer : VoltOf110
{
    public void volt110Off()
    {
        System.Console.WriteLine("DVD Player Off by 110Volt Adapter");
    }
    
    public void volt110On()
    {
        System.Console.WriteLine("DVD Player On by 110Volt Adapter");
    }
}

110Volt To 220 Volt 클래스

public class Converter110To220 : VoltOf220
{
    VoltOf110 iVoltOf110;

    public Converter110To220(VoltOf110 iVoltOf110)
    {
        this.iVoltOf110 = iVoltOf110;
    }

    public void volt220Off()
    {
        iVoltOf110.volt110Off();
    }

    public void volt220On()
    {
        iVoltOf110.volt110On();
    }
}

220 volt 사용 클래스 코드

public class Controller
{
	VoltOf220 ivoltOf220;
	public Controller(VoltOf220 ivoltOf220)
	{
		this.ivoltOf220 = ivoltOf220;
	}
	
	public void turnOn()
	{
		ivoltOf220.volt220On();
	}
	
	public void turnOff()
	{
		ivoltOf220.volt220Off();
	}
}

메인 코드

DVDPlayer dvdPlayer = new DVDPlayer();
Converter110To220 converter110To220 = new Converter110To220(dvdPlayer);
Controller controller = new Controller(converter110To220);
controller.turnOn();
controller.turnOff();

퍼사드 패턴

다양한 인터페이스 코드

public interface Something1
{
    void doSomeThing1();
}

public interface Something2
{
    void doSomeThing2();
}

public interface Something3
{
    void doSomeThing3();
}

public interface Something4
{
    void doSomeThing4();
}

다양한 인터페이스 구현 코드

public class Something1_object : Something1
{
    public Something1_object()
    {
    }
    
    public void doSomeThing1()
    {
        System.Console.WriteLine("DoOne");
    }
}

public class Something2_object : Something2
{
    public Something2_object()
    {
    }
    
    public void doSomeThing2()
    {
        System.Console.WriteLine("DoTwo");
    }
}

public class Something3_object : Something3
{
    public Something3_object()
    {
    }
    
    public void doSomeThing3()
    {
        System.Console.WriteLine("DoThree");
    }
}

public class Something4_object : Something4
{
    public Something4_object()
    {
    }
    
    public void doSomeThing4()
    {
        System.Console.WriteLine("DoFour");
    }
}

모든 인터페이스를 통합 실행하는 클래스 코드

public class AllInOne
{
    Something1 isomething1;
    Something2 isomething2;
    Something3 isomething3;
    Something4 isomething4;

    public AllInOne(
    Something1 isomething1,
    Something2 isomething2,
    Something3 isomething3,
    Something4 isomething4)
    {
        this.isomething1 = isomething1;
        this.isomething2 = isomething2;
        this.isomething3 = isomething3;
        this.isomething4 = isomething4;
    }

    public void doAll()
    {
        this.isomething1.doSomeThing1();
        this.isomething2.doSomeThing2();
        this.isomething3.doSomeThing3();
        this.isomething4.doSomeThing4();
    }
}

메인코드

Something1_object something1 = new Something1_object();
Something2_object something2 = new Something2_object();
Something3_object something3 = new Something3_object();
Something4_object something4 = new Something4_object();
AllInOne allInOne = new AllInOne(something1, something2, something3, something4);
allInOne.doAll();

코드 설명

어댑터 패턴

서로 다른 인터페이스 코드 : 기존 110 volt 인터페이스와 새롭게 교체할 220 volt의 인터페이스이다.

기존의 VoltOf110 인터페이스로 구현된 코드 : 기존 110 volt의 인터페이스를 상속받아 구현한 클래스이다.

110 Volt To 220 Volt 클래스 : 기존의 110 volt 구현체를 받아서 220 volt의 구현 함수로 매칭시켜준다.

메인 코드 : 어뎁터 패턴의 사용 방법을 보여준 코드이다.

퍼사드 패턴

다양한 인터페이스 코드 : 서로 다른 인터페이스를 보여준다.

다양한 인터페이스 구현 코드 : 서로 다른 인터페이스들의 구현체들을 보여준다.

모든 인터페이스를 통합 실행하는 클래스 코드 : 서로 다른 인터페이스를 모두 생성자에서 등록한 후 하나의 함수로 실행시켜 준다.

메인 코드 : 퍼사드 패턴의 사용 방법을 보여준 코드이다.

설명 보충을 위한 그림 자료

어댑터 패턴

110 volt 어뎁터, 110 volt To 220 volt 변환 어뎁터, 220 volt 콘센트이다.

어뎁터 패턴은 이처럼 기존에 맞기 않는 인터페이스 규격을 맞춰주는 패턴이다. 

퍼사드 패턴

퍼사드 패턴은 복잡하게 산재되어 있는 인터페이스들을 취합하여 실행하고 관리를 도와주는 패턴이다. 

어뎁터 패턴과 달리 하나의 인터페이스를 다룬다기보다는 여러 인터페이스를 받아서 좀 더 고도화시키는 패턴이다. 매크로 기능이랑 비슷하다.

필요에 따라 추가 설명

x

정리 및 결론

어댑터 패턴은 기존에 규격에 맞지 않는 경우 사용하는 패턴이다. 내 개인적인 생각으로 이 패턴은 매우 많이 쓰면 관리가 힘들 것 같다. 어댑터 패턴은 단방향?으로 구조여서 변환해야 할 클래스가 늘어날수록 코드가 많아질 것이다. 따라서 어댑터 패턴을 적용하기 전에 한 번쯤 몇 개의 클래스를 변환시켜야 하는지 판단해야 할 것 같다.

퍼사드 패턴은 여러 개로 산재되어 있는 인터페이스 구현체들을 가져와서 하나로 통합하는 패턴이다. 매크로 기능?? 을 수행할 때 필요할 것 같다. 하지만 굳이..? 커맨드 패턴에서 이미 매크로 같은 기능을 구현했는데 이 부분은 잘 모르겠다. 지금 알 수 있는 다른 점은 커맨드 패턴은 하나의 커맨드 인터페이스로 구현된 다양한 구현체를 다룰 때 사용했고, 퍼사드 패턴은 여러개의 인터페이스를 다룰때 사용했다.

'Program > SW Design Patterns' 카테고리의 다른 글

Mediator 패턴 (C++)  (0) 2024.07.31
CH8 스테이트 패턴  (0) 2023.12.04
CH6 커맨드 패턴  (0) 2023.03.07
CH5 데코레이터 패턴  (1) 2023.03.05
CH4 팩토리 패턴  (0) 2023.03.04

 

 

커맨드 패턴 정의

요청 사항을 객체로 캡슐화하여 요청 사항 인스턴스를 체계적으로 관리하는 패턴이다. 체계적인 관리를 통해 요청 사항 실행뿐만 아니라 로그 기록, Undo 기능 등을 관리할 수 있다.

필요한 상황

1. Undo 기능, Log 기록, Macro 기능을 구현하고 관리하고 싶을때

2. 여러 요청 사항을 하나의 인스턴스에서 관리하고 싶을 때

3. 고객사의 요청 사항이 지속적으로 늘어나는 프로그램인 경우

Class 다이어그램

코드

Icommand 인터페이스

public interface Icommand
{
    void excute();
    void undo();
}

요청 사항 객체들

public class NullCommand : Icommand
{
    public void excute()
    {
        Console.WriteLine("It's a null slot");
    }
    
    public void undo()
    {
        Console.WriteLine("It's a null slot");
    }
}

public class ExampleOneClass : Icommand
{
    public void excute()
    {
        Console.WriteLine("It's ExampleOneClass excute method");
    }
    
    public void undo()
    {
        Console.WriteLine("It's ExampleOneClass undo method");
    }
}

public class ExampleTwoClass : Icommand
{
    public void excute()
    {
        Console.WriteLine("It's ExampleTwoClass excute method");
    }
    
    public void undo()
    {
        Console.WriteLine("It's ExampleTwoClass undo method");
    }
}

public class MacroClass : Icommand
{
    List<Icommand> icommands; 
    public MacroClass(List<Icommand> icommands)
    {
        this.icommands = icommands;
    }
    
    public void excute()
    {
        this.icommands.ForEach(x => x.excute());
    }
    
    public void undo()
    {
        this.icommands.ForEach(x => x.undo());
    }
}

중앙 관리 클래스

public class Invoker
{
	List<Icommand> excuters;
	int preSlot;
	public Invoker(int slotSize)
	{
		excuters = new List<Icommand>();
		
		for (int i = 0; i < slotSize; ++i)
		{
			excuters.Add(new NullCommand());
		}
	}
	
	public void setCommand(int slot, Icommand command)
	{
		excuters[slot] = command;
	}
	
	public void undo()
	{
		excuters[preSlot].undo();
	}
	
	public void excute(int slot)
	{
		excuters[slot].excute();
		this.preSlot = slot;
	}
}

main 코드

Invoker invoker = new Invoker(3);
invoker.setCommand(0, new ExampleOneClass());
invoker.setCommand(1, new ExampleTwoClass());
invoker.excute(0);
invoker.excute(1);
invoker.undo();
MacroClass macroClass = new MacroClass(new List<Icommand>() { new ExampleOneClass(), new ExampleTwoClass(), new ExampleOneClass() }); 
invoker.setCommand(2, macroClass);
invoker.excute(2);
invoker.undo();

코드 설명

1. Icommand interface를 통해 중앙 관리 클래스 (Invoker class)는 다른 요청 사항 클래스들을 관리한다. 중요한 건 중앙 관리 클래스는 요청 사항 클래스들의 Icommand interface 에 의해 이용할 수 있는 함수만 알고 다른 것은 관여하지 않는다.

2. 요청 사항 클래스들은 Icommand interface 를 상속받기 때문에 Icommand interface의 규격을 맞춰서 코딩한다. 이때 Log 기능, Undo 기능, Excute 기능들을 구현해야 한다.

3. 중앙 관리 클래스는 Icommand interface를 리스트로 저장하고 있다가 실행이 필요할 때 리스트 안에 저장되어 있는 인스턴스를 실행시키면 된다. 중요 한건 요청 사항 클래스들의 내부 변수, 특징을 알 필요 없다는 것이다.

4. main 코드를 통해 해당 패턴을 어떻게 사용하는지 확인할 수 있다.

설명 보충을 위한 그림 자료

중앙 관리 클래스에게 3번째 인덱스의 매크로 요청 사항 클래스를 실행하는 경우

필요에 따라 추가 설명

x

정리 및 결론

1. 커맨드 패턴도 자주 사용하는 패턴이다. 대표적인 사용 사례로 WPF의 ICommand이다. 이를 통해 View와 이벤트 함수를 바인딩한다.

2. 다양한 요청 사항에 대해 Undo, Log, Macro 기능이 필요한 경우 해당 패턴을 사용하면 좋다.

3. 커맨드 패턴은 중앙 관리 클래스 <-> 요청 사항 인터페이스 <-> 요청 사항 클래스로 구분하여 구성과 상속을 적절히 사용한 패턴이다. 

 

'Program > SW Design Patterns' 카테고리의 다른 글

CH8 스테이트 패턴  (0) 2023.12.04
CH7 어댑터&퍼사드 패턴  (0) 2023.03.19
CH5 데코레이터 패턴  (1) 2023.03.05
CH4 팩토리 패턴  (0) 2023.03.04
CH3 옵저버 패턴  (0) 2023.03.03

데코레이터 패턴 정의

파생클래스의 생성자 파라미터에 덮어씌우고자 하는 인스턴스를 매개변수로 넣음으로써 재귀적으로 현재 인스턴스의 상태를 추가하는 방법이다.  

필요한 상황

1. Open-Closed-Principle(OCP) 원칙을 지키면서 인스턴스의 현재 상태를 추가하고 싶을 때

2. 인스턴스의 상태를 자유롭게 누적시키는 로직이 필요할 때

Class 다이어그램

코드

1. Pizza class 코드

public abstract class Pizza
{
    protected string? strDescription;
    
    public abstract String getDescription();
    
    public abstract double cost();
}

2. Topping class 코드

public abstract class Topping : Pizza
{
    public override String getDescription() => this.strDescription;
}

3. Pizza class의 파생 코드

public class CheesePizza : Pizza
{
    public CheesePizza()
    {
        base.strDescription = "CheesePizza";
    }
    
    public override double cost()
    {
        return 2.33;
    }
    
    public override string getDescription()
    {
        return base.strDescription;
    }
}

public class MeatLoverPizza : Pizza
{
    public MeatLoverPizza()
    {
        base.strDescription = "MeatLoverPizza";
    }
    
    public override double cost()
    {
        return 3.5;
    }
    
    public override string getDescription()
    {
        return base.strDescription;
    }
}

4. Topping class의 파생 코드

public class CheeseTopping : Topping
{
	Pizza pizza;
	public CheeseTopping(Pizza pizza)
	{
		this.pizza = pizza;
	}
	
	public override double cost() => (pizza.cost() + 0.5);
	public override String getDescription() => (pizza.getDescription() + " CheeseTopping");
}

public class TomatoTopping : Topping
{
	Pizza pizza;
	public TomatoTopping(Pizza pizza)
	{
		this.pizza = pizza;
	}
	
	public override double cost() => this.pizza.cost() + 0.2;
	
	public override String getDescription() => this.pizza.getDescription() + " Tomato Topping";

}

5. main 코드

Pizza pizza = new CheesePizza();
Console.WriteLine($"{pizza.getDescription()} : {pizza.cost()}");
pizza = new CheeseTopping(pizza);
Console.WriteLine($"{pizza.getDescription()} : {pizza.cost()}");
pizza = new CheeseTopping(pizza);
Console.WriteLine($"{pizza.getDescription()} : {pizza.cost()}");
pizza = new TomatoTopping(pizza);
Console.WriteLine($"{pizza.getDescription()} : {pizza.cost()}");

코드 설명

1. Pizza class는 파생 클래스 모두 공통으로 사용할 클래스이다.

2. Topping class 는 Pizza class의 파생 클래스이다. Topping class는 계속 이전 인스턴스를 인자로 받아서 추가로 데이터를 추가한다.

3. CheesePizza, MeatLoverPizza class들은 Pizza class의 파생 클래스로써 기본 Pizza의 대분류로 나눈 것이다. 그 이상 그 이하도 없다.

4. Cheese Topping, Tomato Topping class들은 Topping class의 파생 클로스로써 중요한 점은 Pizza class의 인스턴스를 생성자에서 받아서 이전 상태의 인스턴스를 저장한다. 저장한 인스턴스는 다음 함수 호출 시 사용한다. 이는 결국 재귀적으로 내부 Pizza class의 인스턴스를 재귀적으로 호출하는 역할을 수행하게 된다.

5. 사용하는 코드이다.

설명 보충을 위한 그림 자료

클래스 다이어그램 하단에 별도 기입..

필요에 따라 추가 설명

딱히 어려운 건 없다.

정리 및 결론

데코레이터 패턴도 다양한 언어, API에서 제공하는 패턴 중 하나이다. 하지만 재귀함수적 특징으로 인해 코드의 가독성이 떨어지는 단점이 있다. 사용할지 말지는 본인 판단!!

'Program > SW Design Patterns' 카테고리의 다른 글

CH7 어댑터&퍼사드 패턴  (0) 2023.03.19
CH6 커맨드 패턴  (0) 2023.03.07
CH4 팩토리 패턴  (0) 2023.03.04
CH3 옵저버 패턴  (0) 2023.03.03
CH2 싱글턴 패턴  (0) 2023.03.02

 

 

팩토리 패턴 정의

팩토리 패턴의 핵심 : new 예약어를 패턴에 사용한 서브 클래스나, 인터페이스를 상속한 클래스에게 넘기는 방법이다.

 

1. 팩토리 메서드 패턴

클래스의 인스턴스 생성을 서브 클래스에게 결정시키는 방법이다. (서브클래스를 이용한 방법)

2. 추상 팩토리 패턴 (클래스 다이어그램 까지만 포스팅)

인터페이스를 상속한 클래스에게 인스턴스 생성을 시키는 방법이다. (인터페이스를 이용한 방법)

 

필요한 상황

1. new 예약어를 최대한 인계하는 코드가 필요할 때 -> new 예약어는 변화가 힘든 프로그램을 만들기 때문

switch(instanceName)
{
    case "1":
    return new InstanceOne();

    case "2":
    return new InstanceTwo();
 
    case "3":
    return new InstanceThree();
    
    ...
}

향후 코드 수정이 무조건 필요한 팩토리 함수..

2. 인스턴스 생성 함수가 필요하고, 인스턴스의 종류는 가변인 경우

3. 카테고리화한 비즈니스 모델에 대한 인스턴스 생성을 체계화하고 싶은 경우

Class 다이어그램

1. 팩토리 메서드 패턴

 

 

2. 추상 팩토리 패턴

코드

1. BuisnessModel class

public abstract class BuisnessModel
{
    public string Name { get; set; }
    public int Number { get; set; }
    
    public abstract void doSomething1();
    public abstract void doSomething2();
    
    public override string ToString()
    {
        return $"{Name} : {Number}";
    }
}

2. BuisnessModel class의 sub class들 (팩토리에서 생성할 다양한 모델)

public class FactoryMethodPattern_SubOne_BuisnessOneModel : BuisnessModel
{
    public override void doSomething1()
    {
        Console.WriteLine($"do FactoryMethodPattern_SubOne_BuisnessOneModel job 1");
    }
    
    public override void doSomething2()
    {
        Console.WriteLine($"do FactoryMethodPattern_SubOne_BuisnessOneModel job 2");
    }
}

public class FactoryMethodPattern_SubOne_BuisnessTwoModel : BuisnessModel
{
    public override void doSomething1()
    {
        Console.WriteLine($"do FactoryMethodPattern_SubOne_BuisnessOneModel job 1");
    }
    
    public override void doSomething2()
    {
        Console.WriteLine($"do FactoryMethodPattern_SubOne_BuisnessTwoModel job 2");
    }
}

public class FactoryMethodPattern_SubTwo_BuisnessOneModel : BuisnessModel
{
    public override void doSomething1()
    {
        Console.WriteLine($"do FactoryMethodPattern_SubTwo_BuisnessOneModel job 1");
    }
    
    public override void doSomething2()
    {
        Console.WriteLine($"do FactoryMethodPattern_SubTwo_BuisnessOneModel job 2");
    }
}

public class FactoryMethodPattern_SubTwo_BuisnessTwoModel : BuisnessModel
{
    public override void doSomething1()
    {
        Console.WriteLine($"do FactoryMethodPattern_SubTwo_BuisnessOneModel job 1");
    }
    
    public override void doSomething2()
    {
        Console.WriteLine($"do FactoryMethodPattern_SubTwo_BuisnessTwoModel job 2");
    }
}

3. InstanceStore 추상 Class

public abstract class BaseInstanceStore
{
    public BuisnessModel GetBuisnessModel(string buisness_type)
    {
        BuisnessModel bm = OrderBuisnessModel(buisness_type);
        bm.doSomething1();
        bm.doSomething2();
        return bm;
    }
	
    protected abstract BuisnessModel OrderBuisnessModel(string buisness_type);
}

4. SubInstanceStore Class 들

public class SubOneInstanceStore : BaseInstanceStore
{  
    protected override BuisnessModel OrderBuisnessModel(string buisness_type)
    {
        switch(buisness_type)
        {
            case "ONE":
                return new FactoryMethodPattern_SubOne_BuisnessOneModel() { Name = "FactoryMethodPattern_SubOne_BuisnessOneModel of SubOneInstanceStore", Number = 0 };
            case "TWO":
                return new FactoryMethodPattern_SubOne_BuisnessTwoModel() { Name = "FactoryMethodPattern_SubOne_BuisnessTwoModel of SubOneInstanceStore", Number = 0 };
            default:
                return new FactoryMethodPattern_SubOne_BuisnessOneModel() { Name = "FactoryMethodPattern_SubOne_BuisnessOneModel of SubOneInstanceStore", Number = 0 };
        }
    }
}

public class SubTwoInstanceStore : BaseInstanceStore
{  
    protected override BuisnessModel OrderBuisnessModel(string buisness_type)
    {
        switch(buisness_type)
        {
            case "ONE":
                return new FactoryMethodPattern_SubTwo_BuisnessOneModel() { Name = "FactoryMethodPattern_SubTwo_BuisnessOneModel of SubOneInstanceStore", Number = 0 };
            case "TWO":
                return new FactoryMethodPattern_SubTwo_BuisnessTwoModel() { Name = "FactoryMethodPattern_SubTwo_BuisnessTwoModel of SubOneInstanceStore", Number = 0 };
            default:
                return new FactoryMethodPattern_SubTwo_BuisnessOneModel() { Name = "FactoryMethodPattern_SubTwo_BuisnessOneModel of SubOneInstanceStore", Number = 0 };
        }
    }
}

5. Main 코드

BaseInstanceStore instanceOneFactory = new SubOneInstanceStore();
BuisnessModel bm1 = instanceOneFactory.GetBuisnessModel("ONE");
Console.WriteLine(bm1.ToString());

BaseInstanceStore instanceTwoFactory = new SubTwoInstanceStore();
BuisnessModel bm2 = instanceTwoFactory.GetBuisnessModel("TWO");
Console.WriteLine(bm2.ToString());

코드 설명

1. BuisnessModel Class는 우리가 new 예약어를 통해 실제 인스턴스를 생성할 Class이다. 향후 다양한 파생 클래스의 베이스 클래스로 이용할 것이다. 

2. BuisnessModel class의 sub class들이다. 다양한 모델로 추가 삭제 가능하다.

3. InstanceStore 추상 Class이다. 파생 클래스로 BuisnessModel을 첫 번째로 대분류 한다.

4. InstanceStore의 파생 클래스이다. 파생 클래스로 BuisnessModel을 대분류 하고 파생 클래스의 함수를 통해 소분류로 BuisnessModel을 나눈다.

5. Main 코드는 팩토리 메서드 패턴을 사용하는 방법이다.

 

-- 추상 팩토리 패턴은 생략-- (팩토리 메소드 패턴을 많이 이용하기 때문..) 

설명 보충을 위한 그림 자료

Instance 요청 흐름 (초록선 : 요청 흐름, 보라 : 출력 흐름)

필요에 따라 추가 설명

1. 팩토리 패턴은 우리가 정의한 비즈니스 모델이 많을수록 빛을 발한다.

2. 비즈니스 모델이 추가될 경우 수정해야 하는 부분은 대분류 한 InstanceFactory의 내부 함수이다. 

정리 및 결론

단순 팩토리 함수(switch문으로 만든)와 팩토리 패턴을 비교했을 때 비즈니스 모델의 추가 삭제 요인이 적다면 단순 팩토리 함수가 유용해 보인다. 따라서 우리는 단순 팩토리 함수와 팩토리 패턴을 구분하고 장단점을 따지고 구현할지 말지 판단해야 한다.

코더가 아닌 개발자가 되자!!

'Program > SW Design Patterns' 카테고리의 다른 글

CH6 커맨드 패턴  (0) 2023.03.07
CH5 데코레이터 패턴  (1) 2023.03.05
CH3 옵저버 패턴  (0) 2023.03.03
CH2 싱글턴 패턴  (0) 2023.03.02
CH1 전략 패턴  (0) 2023.03.02

 

옵저버 패턴 정의

제공자 (subject), 구독자 (subscriber)로 구분하여 다룬다. 제공자는 자신의 상태가 변할 경우 자신을 구독한 구독자에게 알리는 역할을 수행한다. 1 대 다 의존성을 가지는 패턴이다.

필요한 상황

1. 제공자의 정보가 변할 때마다 모든 구독자에게 알림을 보내야 하는 경우
2. 구독자의 리소스를 아껴야 할 경우 (제공자에게 데이터를 계속 요청하기보다는 콜백형태로 제공자에게 수동적으로 받기 때문)
3. 구독자 Class와 제공자 Class 간 결합성을 낮추고 싶은 경우

Class 다이어그램

코드

1. ISubject와 IObserver interface 코드

public interface ISubject
{
    void RegistUser(IObserver observer);
    void RemoveUser(IObserver observer);
    void Broadcast();
}

public interface IObserver
{ 
    void update(object sender, EventArgs evetArgs);
}

2. BroacastSubject Class 코드

public class BroadcastSubject : ISubject
{
    private event EventHandler broadcast = null;
    
    public int nCurrentState { get; private set; }
    
    public void Broadcast()
    {
        if (broadcast != null)
        {
            broadcast(this, null);
        }
        else
        {
            Console.WriteLine("no Observer..");
        }
    }
    
    public void ChangeValue(int nInputData)
    {
        nCurrentState = nInputData;
        Console.WriteLine("===========Change Value! Let's Broacast to my Observer!! =============");
        this.Broadcast();
    }
    
    public void RegistUser(IObserver observer)
    {
        broadcast += observer.update;
    }
    
    public void RemoveUser(IObserver observer)
    {
        broadcast -= observer.update;
    }
}

3. Client_User Class 코드

public class Client_User : IObserver
{
    public string strClientUserName = "";
    public Client_User(string strClientUserName, ISubject subject)
    {
        this.strClientUserName = strClientUserName;                
        subject.RegistUser(this);
    }
    
    public void update(object sender, EventArgs eventArgs)
    {
        var broadcaster = sender as BroadcastSubject;
        Console.WriteLine($"=============================================================");
        Console.WriteLine($"{this.strClientUserName} GetData : {broadcaster.nCurrentState}");
    }
}

4. Main 코드

BroadcastSubject broadcastSubject = new BroadcastSubject();
broadcastSubject.ChangeValue(3);

Client_User cu1 = new Client_User("user1", broadcastSubject);
broadcastSubject.ChangeValue(3);

Client_User cu2 = new Client_User("user2", broadcastSubject);
broadcastSubject.ChangeValue(4);

broadcastSubject.RemoveUser(cu2);
broadcastSubject.ChangeValue(5);

코드 설명

1. ISubject, IOversber interface 코드이다. 각 역할별로 필수로 구현해야 하는 함수를 정의했다. 
2. ISubject interface를 적용시킨 BroadcastSubject 코드이다. EventHandler는 C#에서 제공하는 예약어로 해당 EventHandler에 아래와 같이 규격을 맞춘 함수를 등록하면 EventHandler 내부의 invocationlist에 등록되어 해당 EventHandler만 호출하면 등록한 함수를 모두 호출한다. 규격과 이용방법은 아래와 같다.

class TestClass
{
    public event EventHandler broadcast = null;
    
    void broacastToObserver()
    {
        if (broadcast != null)
        {
            broacast(this, null);
        }
    }
}

class ObserverClass()
{
    public void update(object sender, EventArgs eventArgs)
    {
        //do something..
    }
}

TestClass testClass = new TestClass();
ObserverClass obClass = new ObserverClass();

testClass.broacast += obClass.update; //EventHandler에 함수 등록

testClass.broacastToObserver(); //EventHandler 호출!!!!!!!

3. Client_User Class 코드의 특징점은 Observer 입장으로 보자면 생성자에서 Broadcast subject 인스턴스를 참조로 받아와서 자신을 등록하고, 이후 update 함수를 콜백형태로 받는 구조이다.
 

설명 보충을 위한 그림 자료

필요에 따라 추가 설명

1. 옵저버 패턴은 정말 많이 쓰인다. WPF에서도 사용자가 정의한 Class에 대해서 ObservableCollection 등 쓰이는데 이는 모두 GUI 상에 나와있는 버튼으로부터 실시간으로 알림을 받기 위해서다. (사실 정확히 뭐가 Subject이고 Observer 인지는 모른다)
2. 콜백 형태와 매우 유사하다.

정리 및 결론

오늘은 옵저버 패턴을 공부했다. 제법 아는 척하기 좋은 패턴이고 이미 많이 상용화되어 있다. 우리가 직접 이 패턴을 구현해야 할 때는 별로 없을 것 같다. 하지만 이런 패턴으로 구현되어 있는 구조를 본다면 빨리 알아보고 활용할 수 있는 개발자가 되자!!

'Program > SW Design Patterns' 카테고리의 다른 글

CH5 데코레이터 패턴  (1) 2023.03.05
CH4 팩토리 패턴  (0) 2023.03.04
CH2 싱글턴 패턴  (0) 2023.03.02
CH1 전략 패턴  (0) 2023.03.02
개요  (0) 2023.03.01

싱글턴 패턴 정의

하나의 프로그램에서 오직 1개의 인스턴스만을 다루는 패턴이다. 다른 모든 인스턴스에서는 싱글턴 패턴의 인스턴스를 어디서든 끌어와서 쓸 수 있다. 생성자의 접근 권한이 private로 한정되어 있어서 다른 인스턴스에서는 직접적으로 인스턴스를 생성시키지 못한다.

필요한 상황

1. 모든 인스턴스에서 동일한 상태의 인스턴스가 필요한 경우
2. 1개의 인스턴스로 충분한 경우 (일례 : 프로그램 설정정보, 쓰레드 풀)
3. 2개 이상의 인스턴스 생성을 막기 위한 경우

Class 다이어그램

코드

1. Helper Class

public class Helper
{
	static Helper _helperInstance = new Helper();

	private Helper() { }

	static public Helper getInstance()
	{
		return _helperInstance;
	}

	public void UseThis()
	{
		Console.WriteLine("HI I'm singleton instance!");
	}
}

2. main code

Helper.getInstance().UseThis();

 

코드 설명

1. Helpclass
정의한 Class 내부에 static 예약어로 자기 자신의 인스턴스를 할당하였다. 이는 전연변수처럼 프로그램을 시작할때 메모리에 미리 할당하여 사용한다는 의미이다. 그외 특이점은 생성자의 접근 제한자를 private으로 정의하여 자기 자신을 제외하고 생성을 못하게 하였다.
해당 싱글턴 패턴의 인스턴스를 가져오기 위해서는 오직 getInstance 함수를 통해 가져올 수 있다.
 
2. main code
싱글턴 패턴의 인스턴스를 사용하는 방법이다.

설명 보충을 위한 그림 자료

필요에 따라 추가 설명

싱글턴 패턴은 전역 변수와 유사하다. 하지만 몇가지 차이점이 있다.

  차이점 1. 전역 변수는 결국 Class 외부에서 new 예약어를 통해 메모리를 할당하기 때문에 1개 이상의 인스턴스를 생성할 수 있다. 이는 프로그램에 오직 1개의 인스턴스를 생성하려하는 부분을 충족시키지 못한다.
  차이점 2. Class 내부에서 생성을 다루기 때문에 아래 코드와 같이 인스턴스를 사용하기 직전에 생성시키는 방법을 사용할 수 있다.(lazy initialization

public class Helper
{
	static Helper _helperInstance;

	private Helper() { }

    
	static public Helper getInstance()
	{
		lock(typeof(Helper))
		{
			if (_helperInstance == null)
			{
				_helperInstance = new Helper();
			}
		}

		return _helperInstance;
	}

	public void UseThis()
	{
		Console.WriteLine("HI I'm singleton instance!");
	}
}

lock 예약어를 사용하는 이유는 멀티 스레드를 이용할 경우 여러 스레드에서 동시에 생성을 요청할 경우를 막기 위해서 사용하는 것이다.
 

정리 및 결론

1. 싱글턴 패턴은 정말 많이 사용하는 패턴이다.
2. WPF를 사용할때에 MVVM Silverlight 를 사용했었는데 개별 ViewModel의 인스턴스를 관리하는 인스턴스에 사용한 사례를 봤었다. 이처럼 프로그램의 전체를 다루는데(일례 : 서로 다른 ViewModel 간 데이터 교환 등..) 효과적인 패턴이다. 
3. 마지막으로 오늘 탈피한 리챠드 :)

2023년 03월 03일

'Program > SW Design Patterns' 카테고리의 다른 글

CH5 데코레이터 패턴  (1) 2023.03.05
CH4 팩토리 패턴  (0) 2023.03.04
CH3 옵저버 패턴  (0) 2023.03.03
CH1 전략 패턴  (0) 2023.03.02
개요  (0) 2023.03.01

 

전략(Strategy) 패턴 정의

알고리즘 구현 부분을 캡슐화하여 알고리즘의 변경과 수정에 용이한 패턴을 의미한다. 서브 클래스들의 핵심 알고리즘 구현 부분을 별도로 캡슐화하여 베이스 클래스와 서브 클래스 간의 상속으로 인한 결합성을 낮춘다.    

 

핵심 : 베이스 클래스는 핵심 알고리즘의 호출 부분을 인터페이스로 저장하고 호출만수행하고, 핵심 알고리즘의 구현 부분을 캡슐화하여 코딩한다.

필요한 상황

1. 베이스 클래스를 상속 받은 서브 클래스들이 베이스 클래스의 함수를 호출할 때 각기 다른 기능을 수행하는 구조가 필요할 때 (다형성을 이용할 경우)

2. 서브 클래스가 베이스 클래스의 함수를 오버라이딩하여 서브 클래스에서 함수를 재작성 해야하는 경우 (매번 서브 클래스에서 함수를 오버라이딩하여 작성할 경우)

3. 향후 베이스 클래스를 상속받은 서브 클래스가 증가할 것으로 예상 될 경우 (잠재적 기능 추가 요소인 경우)

Class 다이어그램

요구 사항 1. RPG 게임 환경에서 검사, 수도승, 궁사 직업군이 있다.

요구 사항 2. 각 직업군은 서로 다른 공격과 방어 행동을 가진다.

요구 사항 3. 각 직업군은 무기 변경이 가능하다.

코드

 

 

 

1. 사람 Class (베이스 클래스)

public class Human
{
	protected IAttackBehavior attackBehavior { get; set; }
	protected IDefenceBehavior defenceBehavior { get; set; }

	public void Attack()
	{
		attackBehavior.attack();
	}

	public void Defence()
	{
		defenceBehavior.defence();
	}

	public void SetAttackMode(IAttackBehavior ia)
	{
		this.attackBehavior = ia;
	}

	public void SetDefenceMode(IDefenceBehavior id)
	{
		this.defenceBehavior = id;
	}
}

2. 행동 interface 

public interface IDefenceBehavior
{
	void defence();
}
    
public interface IAttackBehavior
{
	void attack();
}

3. 알고리즘 구현 Class 부분 Behavior interface를 상속받음

public class AttackWithBow : IAttackBehavior
{
	public void attack()
	{
		Console.WriteLine("Attack with Bow");
	}
}

public class AttackWithSword : IAttackBehavior
{
	public void attack()
	{
		Console.WriteLine("Attack with Sword");
	}
}

public class AttackWithGlobe : IAttackBehavior
{
	public void attack()
	{
		Console.WriteLine("Attack with Globe");
	}
}

public class DefenceWithBow : IDefenceBehavior
{
	public void defence()
	{
		Console.WriteLine("Defence with Bow");
	}
}

public class DefenceWithSword : IDefenceBehavior
{
	public void defence()
	{
		Console.WriteLine("Defence with Sword");
	}
}

public class DefenceWithGlobe : IDefenceBehavior
{
	public void defence()
	{
		Console.WriteLine("Defence with Globe");
	}
}

4. 전사, 궁사, 수도승 클래스 (서브 클래스)

public class Archer : Human
{
	public Archer()
	{
		this.attackBehavior = new AttackWithBow();
		this.defenceBehavior = new DefenceWithBow();
	}
}

public class SwordMaster : Human
{
	public SwordMaster()
	{
		this.attackBehavior = new AttackWithSword();
		this.defenceBehavior = new DefenceWithSword();
	}
}

public class Monk : Human
{
	public Monk()
	{
		this.attackBehavior = new AttackWithGlobe();
		this.defenceBehavior = new DefenceWithGlobe();
	}
}

5. Main 코드

Attack(new Archer());
Attack(new SwordMaster());
Attack(new Monk());
var instance = new Monk();
instance.SetAttackMode(new AttackWithBow());
Attack(instance);


void Attack(Human human)
{
    human.Attack();
}

void Defence(Human human)
{
    human.Defence();
}

출력 결과

코드 설명

1. 사람 Class (베이스 클래스)

   - 모든 서브 클래스들의 함수와 알고리즘 구현 인터페이스를 담고 있다.

   - Class의 사용자는 서브 클래스들의 특징을 파악할 필요 없고 베이스 클래스의 함수와 특성값만 알면 된다. 

2. 행동 interface

   - 알고리즘 인터페이스이다.

   - 베이스 클래스는 알고리즘 인터페이스 변수를 저장하고, 필요에 따라 인터페이스를 상속받은 다른 구현 알고리즘을 바꿔치기 사용하면 된다.

3. 알고리즘 구현 Class 부분 Behavior interface를 상속받음

   - 알고리즘 인터페이스를 상속받은 Class이다.

   - 실제 알고리즘의 구현이 들어가는 부분이다.

4. 전사, 궁사, 수도승 클래스 (서브 클래스)

   - 생성자 부분에서 행동 interface 규격을 맞추는 Class로 정의하여 베이스 클래스의 행동 interface에 할당하면 된다.

5. Main 코드

   - Main에 정의한 함수는 사람 class의 함수를 호출하지만 각 서브 클래스들로 업캐스팅하여 기입하고 각 서브 클래스들은 각기 다른 행동 interface로 구성했기 때문에 서로 다른 출력 결과를 보여준다.

   - 출력 결과 모든 요구사항을 충족함을 볼 수 있다.

설명 보충을 위한 그림 자료

필요에 따라 추가 설명

1. 베이스 클래스를 상속받아 서브 클래스에서 재사용하는 경우 고려하면 좋다.

2. 상속구조에 의한 베이스 클래스와 서브 클래스 간의 코드 변경에 의한 의존성이 줄어든다.

3. 단 interface라는 추상화 구조가 필요하다.

4. 알고리즘을 사용하는 Class는 interface형 변수를 담고 있다가 필요시 interface에서 정의한 함수를 호출하는 방법이다.

 

정리 및 결론

1. 전략 패턴의 핵심은 베이스 클래스가 특정 interface를 변수로 저장하고 있다가 특정 interface를 상속받은 구현 알고리즘 Class를 변수처럼 사용하는 것

2. 전략 패턴은 서브 클래스에서 베이스 클래스에 구성되어 있는 특정 interface를 원하는 구현 알고리즘으로 할당시킨다.

3. 베이스 클래스를 이용하는 코드(Main 부분)는 베이스 클래스의 함수만 호출해도 다양한 구현 알고리즘을 구성할 수 있다.

4. 객체지향적 사고 팁

    - 변하는 부분은 캡슐화한다. (추가 및 변경이 용이하도록 하는 것)

    - 상속보다는 구성을 이용하면 결합성을 낮출 수 있다. (베이스 클래스가 특정 interface를 변수로 저장하는 것)

    - 직접 구현보다는 interface를 상속받은 구현 Class로 나눠 변수처럼 사용한다.

'Program > SW Design Patterns' 카테고리의 다른 글

CH5 데코레이터 패턴  (1) 2023.03.05
CH4 팩토리 패턴  (0) 2023.03.04
CH3 옵저버 패턴  (0) 2023.03.03
CH2 싱글턴 패턴  (0) 2023.03.02
개요  (0) 2023.03.01

본 카테고리는 SW의 객체지향 접근을 기반으로 하는 SW 디자인 패턴을 다루는 카테고리이다.
주요 목적은 주어지는 사례에 따라 최적의 디자인 패턴을 선택하고 구현까지 할 수 있는 것을 목표로 한다. 
다룰 디자인 패턴과 글의 구성, 개발환경은 아래와 같다.
 

디자인 패턴 목차

1. 전략 패턴
2. 싱글턴 패턴
3. 옵저버 패턴
4. 팩토리 패턴
5. 데코레이터 패턴
6. 커맨드 패턴
7. 어댑터&퍼사드 패턴
8. 스테이트 패턴
9. MVC, MVVM 패턴
 

챕터 구성

1. 패턴 정의
2. 필요한 상황
3. Class 다이어그램
4. 코드
5. 코드 설명
6. 설명 보충을 위한 그림 자료
7. 필요에 따라 추가 설명
8. 정리 및 결론
 

개발 환경

1. Language : C#
2. OS : Windows 11
3. IDE : Visual studio 2019
 

작성 기간

2023-03-02 ~ 2023-03-XX (X일)
 
마지막으로 리챠드 :)

2023년 03월 01일 리챠드

 
 
 
 
 
 

'Program > SW Design Patterns' 카테고리의 다른 글

CH5 데코레이터 패턴  (1) 2023.03.05
CH4 팩토리 패턴  (0) 2023.03.04
CH3 옵저버 패턴  (0) 2023.03.03
CH2 싱글턴 패턴  (0) 2023.03.02
CH1 전략 패턴  (0) 2023.03.02

+ Recent posts