class Person
{
private:
    const string _name;
    float _weight;
    float _height;
public:
    Person(const string& name, float weight, float height)
        : _name(name), _weight(weight), _height(height)
    {
    }
    
    float getWeight(/*const Person* this */) const
    {
        return _weight;
    }
};

int main()
{
    const Person person0("Donghee", 84.f, 178.8.f);
    person0.getWeight();
}

Person class의 getWeight에 const 키워드를 붙임으로써 Class 사용자는 Person Class를 Const로 선언하고 해당 멤버함수를 사용할 수 있다.

 

새로 배운 점

1. Class의 멤버함수는 암시적으로 this 포인터를 받는다.

2. Class를 const로 선언하는 사용하는 것을 대비할 때에는 get 함수에 대해 const 키워드를 붙이자. 

'Program > C,C++' 카테고리의 다른 글

연산자 오버로딩  (0) 2023.07.02
멤버 함수 포인터  (0) 2023.06.28
this 포인터를 이용한 빌더 패턴  (0) 2023.06.26
Class 전방 선언  (0) 2023.06.26
struct Transaction
{
    const int txID;
    const int fromID;
    const int toID;
    const int value;
}

class TransactionBuilder
{
private:
    int _fromID;
    int _toID;
    int _value;
    
public:
    Transaction build()
    {
    	int txID = _fromID ^ _toID ^ _value;
        return Transaction {txID, _fromID, _toID, _value};
    }
    
    TransactionBuilder& setFromID(int fromID)
    {
    	_fromID = fromID;
        return *this;
    }
    
    TransactionBuilder& settoID(int toID)
    {
    	_toID = toID;
        return *this;
    }
    
    TransactionBuilder& setvalue(int value)
    {
    	_value = value;
        return *this;
    }
}

int main()
{
    Transaction ts = TransactionBuilder().
    setFromID(10).
    settoID(20).
    setvalue(30).
    build();
}

 

 

this 포인터를 이용한 빌더 패턴이다.

 

set 함수들의 반환이 객체의 참조를 반환하는데 이때 현재 객체의 포인터 지정자를 반환하여 자기 자신을 반환하는 원리이다.

'Program > C,C++' 카테고리의 다른 글

연산자 오버로딩  (0) 2023.07.02
멤버 함수 포인터  (0) 2023.06.28
const 멤버 함수  (0) 2023.06.28
Class 전방 선언  (0) 2023.06.26

Village.h 파일 코드

#include <vector>

Class Person;
Class Village
{
private:
	std::vector<Person> persons;
public:
	void add(Person);
}

Village.cpp 파일 코드

#include "Village.h"
#include "Person.h"

void Village::add(Person person)
{
	persons.push_back(person)
}

 

1. Village 는 Person class를 사용한다. 하지만 Person의 Header 파일을 사용하지 않는다.

-> 의존 관계를 풀기 위해 -> Person의 코드가 수정될 경우 Village 까지 연쇄적으로 재빌드 해야함.

2. Village의 add 함수는 Village의 Header파일에서 선언만하고 정의는 Village.cpp 파일에서 정의함.

 

 

'Program > C,C++' 카테고리의 다른 글

연산자 오버로딩  (0) 2023.07.02
멤버 함수 포인터  (0) 2023.06.28
const 멤버 함수  (0) 2023.06.28
this 포인터를 이용한 빌더 패턴  (0) 2023.06.26

시스템 요구 사항

1. 강화학습 알고리즘이 있어야하고, 커스터 마이징 가능해야 한다.

   -> 강화학습 적용의 확장성

2. Unity 와 같은 시뮬레이션과 연동 가능해야한다.

   -> 환경 적용의 확장성, 가시화 상승

3. 커뮤니티가 활성화 되어 있어야한다.

   -> 많은 정보량

4. 병렬 환경에서도 구동 가능해야한다.

   -> 학습 가속

5. ROS2 와 연동했을때 문제 없어야한다.

   -> 실제 로봇 적용을 고려

 

강화학습 프레임워크 후보

 

1. Ray RLlib


장점:

다양한 알고리즘 지원: RLlib은 다양한 강화 학습 알고리즘을 지원합니다. 이는 연구자가 다양한 알고리즘을 쉽게 실험해 볼 수 있게 합니다.
분산 학습 지원: RLlib은 분산 학습을 지원하며, 이를 통해 대규모의 학습 작업을 처리할 수 있습니다.
다양한 환경 지원: RLlib은 OpenAI Gym 인터페이스를 따르는 다양한 환경을 지원합니다.

 

단점:

복잡성: RLlib은 매우 강력한 도구이지만, 그만큼 복잡성도 높습니다. 이로 인해 학습 곡선이 가파를 수 있습니다.
문서화: RLlib의 문서화는 개선이 필요한 부분이 있습니다. 특히, 고급 기능에 대한 문서화가 부족할 수 있습니다.

https://docs.ray.io/en/master/rllib/

 

RLlib: Industry-Grade Reinforcement Learning — Ray 3.0.0.dev0

Get started with environments supported by RLlib, such as Farama foundation’s Gymnasium, Petting Zoo, and many custom formats for vectorized and multi-agent environments.

docs.ray.io

 

2. Acme

 

https://www.deepmind.com/publications/acme-a-new-framework-for-distributed-reinforcement-learning

 

Acme: A new framework for distributed reinforcement learning

Reinforcement Learning (RL) provides an elegant formalization for the problem of intelligence. In combination with advances in deep learning and increases in computation, this formalization has resulted in powerful solutions to longstanding artificial inte

www.deepmind.com

 

장점:

모듈성: Acme는 간단하고 모듈식 구성 요소를 사용하여 다양한 규모의 실행에서 사용할 수 있는 에이전트를 구축하도록 설계되었습니다. 이는 연구자가 새로운 아이디어를 빠르게 프로토타입화하고 게시된 RL 알고리즘을 재현하는 데 도움이 됩니다.
재현성: Acme는 중요하거나 최신의 알고리즘에 대한 간단한 참조 구현을 제공합니다. 이는 Acme의 설계 결정의 유효성을 입증하는 데 도움이 되며, RL 연구의 재현성에 중요한 기여를 합니다.

 

단점:

알고리즘 지원: Acme는 RLlib에 비해 지원하는 알고리즘의 수가 적습니다. 이는 특정 알고리즘을 사용하려는 연구자에게는 제한적일 수 있습니다.
분산 학습 지원: Acme는 RLlib에 비해 분산 학습 지원이 덜 발달되어 있습니다. 이는 대규모 학습 작업을 처리하는 데 제한적일 수 있습니다.

어댑터 패턴 정의

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

퍼사드 패턴 정의

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

필요한 상황

어댑터 패턴

- 새로운 인터페이스를 이미 개발되어 있는 인터페이스로 변환시키고 싶을 때 (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

+ Recent posts