article thumbnail image
Published 2020. 11. 24. 20:28
  • 추상 클래스를 상속받았지만, 베이스 클래스에 사용하지 않는 함수임에도, 구현해야 한다면, 어떤 원칙이 위배되었는가?
    • ISP (인터페이스 분리 원칙)
  • '사과' 클래스는 '과일' 클래스로부터 상속받았다. '과일'을 매개변수로 하는 함수는 '사과' 에 대해서는 정상적으로 작동하지 않는다. 어떤 원칙에 위배되었는가?
    • LSP (리스코프 치환 원칙)

 

  •  SOLID
    • Single Responsibility Principle(SRP) - 단일 책임 원칙
      • A class shold only have a single responsibility
      • 단일 객체가 여러개의 책임을 지고 있지 않는가?
    • Open-Closed Princible(OCP) - 개방-폐쇄 원칙
      • Entities should be open for extension but closed for modification
      • 기존의 코드를 수정하지 않으면서, 기능을 추가할 수 있는가?
    • Liskov Substitution Principle(LSP) - 리스코프 치환 원칙
      • Objects should be replaceable with instances of their subtypes without altering program correctness
      • 부모 클래스 객체를 파생 클래스객체로 치환했을때에도, 제대로 작동하는가?
    • Interface Segmentation Principle(ISP) - 인터페이스 분리 원칙
      • Many client-specific interfaces better that one general-purpose interface. No client should be forced to depend on methods it does not use.
      • 인터페이스들의 기능이 겹치지는 않는가?
    • Dependency Inversion Principle(DIP) - 의존 역전 원칙
      • Dependencies should be abstract rather that concrete
      • High-level modules should not depend on low-level modules. Both should depend on abstractions.
      • Dependencies on interfaces and supertypes is better than dependencies on concrete types.
        • Inversion of Control(IoC) : the actual process of creating abstractions and getting them to replace dependencies.
        • Dependency Injection : use of software frameworks to ensure that a component's dependencies are satisfied.
      • 접근이 용이한가?

 

//Example.1 SRP : 단일 책임 원칙
#include <string>
#include <vector>
#include <fstream>

struct Journal {
	std::string title;
	std::vector<std::string> entries;

	explicit Journal(std::string const& title)
		: title(title) {}

	void add(std::string const& entry) {
		entries.push_back(entry);
	}

	// SRP에 위배 : save해야할 클래스가 수십개씩 있다고 상상하면, 심각성을 깨달을 것이다.
	/*void save(std::string const& filename) {
		std::ofstream ofs(filename);
		for (auto& s : entries) {
			ofs << s << std::endl;
		}
	}*/
};

struct PersistantManager {
	static void save(Journal const& j, std::string const& filename) {
		std::ofstream ofs(filename);
		for (auto& s : j.entries) {
			ofs << s << std::endl;
		}
	}
};

 

//Example.2 OCP : 개방-폐쇄 원칙
#include <string>
#include <vector>
#include <iostream>

enum class Color { Red, Green, Blue };
enum class Size { Small, Medium, Large };

struct Product
{
	std::string name;
	Color color;
	Size size;
};

// OCP 에 위배 : 필터에서 수정을 가능케하기 때문
//struct ProductFilter
//{
//	using Items = std::vector<Product*>;
//
//	static Items by_color(Items items, Color color) {
//		Items result;
//		for (auto& i : items) {
//			if (i->color == color) {
//				result.push_back(i);
//			}
//		}
//		return result;
//	}
//
//	static Items by_size(Items items, Size Size) {
//		Items result;
//		for (auto& i : items) {
//			if (i->size == Size) {
//				result.push_back(i);
//			}
//		}
//		return result;
//	}
//};

template <typename T>
struct ISpecification
{
	virtual bool is_satisfied(T* item) = 0;
};

template <typename T>
struct  IFilter
{
	virtual std::vector<T*> filter(std::vector<T*> items, ISpecification<T>& spec) = 0;
};

struct  BetterFilter : IFilter<Product>
{
	using Items = std::vector<Product*>;
	virtual std::vector<Product*> filter(std::vector<Product*> items, ISpecification<Product>& spec) override
	{
		Items result;
		for (auto& p : items) {
			if (spec.is_satisfied(p)) {
				result.push_back(p);
			}
		}
		return result;
	}
};

struct  ColorSpecification : ISpecification<Product>
{
	Color color;

	explicit ColorSpecification(Color const color) : color{ color } {}

	bool is_satisfied(Product* item) override
	{
		return item->color == color;
	}
};

struct SizeSpecification : ISpecification<Product>
{
	Size size;
	explicit SizeSpecification(Size const size) : size(size) {}

	bool is_satisfied(Product* item) override 
	{
		return item->size == size;
	}
};

template <typename T>
struct AndSpecification : ISpecification<T>
{
	ISpecification<T>& first;
	ISpecification<T>& second;
	explicit AndSpecification(ISpecification<T>& first, ISpecification<T>& second)
		: first(first), second(second) {}

	bool is_satisfied(T* item) override 
	{
		return first.is_satisfied(item) && second.is_satisfied(item);
	}
};

auto main() -> int {
	Product apple{ "Apple", Color::Green, Size::Small };
	Product tree{ "Tree", Color::Green, Size::Large };
	Product house{ "House", Color::Blue, Size::Large };

	std::vector<Product*> all{ &apple, &tree, &house };
	
	BetterFilter bf;
	ColorSpecification green(Color::Green);

	auto green_things = bf.filter(all, green);
	for (auto& x : green_things) {
		std::cout << x->name << " is green " << std::endl;
	}

	SizeSpecification large(Size::Large);
	AndSpecification<Product> asp(green, large);
	auto large_and_big_things = bf.filter(all, asp);

	for (auto& x : large_and_big_things) {
		std::cout << x->name << "is large and big " << std::endl;
	}

	return 0;
}

 

//Example.3 LSP : 리스코프 치환 원칙
#include <iostream>
class Rectangle {
protected:
	int width, height;
public:
	Rectangle(int const width, int const height)
		: width(width), height(height) {}
	
	virtual int GetWidht() const { return width; }
	
	virtual int GetHeight() const { return height; }
	
	virtual void SetWidth(int const width) { this->width = width; }
	
	virtual void SetHeight(int const height) { this->height = height; }

	int Area() const { return width * height; }
};

class Square : public Rectangle {
public:
	Square(int size) : Rectangle(size, size) {}
	
	void SetWidth(int const width) override {
		this->width = height = width;
	}

	void SetHeight(int const height) override {
		this->height = width = height;
	}

	//void SetSize(int const size);
};

// LSP 에 위배
void process(Rectangle& r) {
	int w = r.GetWidht();
	r.SetHeight(10);
	std::cout << "expect area = " << w * 10
		<< ", got " << r.Area() << std::endl;
}

// 해결책
struct RectangleFactory {
	static Rectangle CreateRectangle(int w, int h);
	static Rectangle CreateSquare(int size);
};

auto main() ->int {
	Rectangle r{ 5,5 };
	process(r);

	Square s{ 5 };
	process(s);

	return 0;
}

 

//Example.4 ISP : 인터페이스 분리 원칙
#include <vector>
struct Document;

// ISP 에 위배
//struct IMachine {
//	virtual void print(std::vector<Document*>docs) = 0;
//	virtual void scan(std::vector<Document*>docs) = 0;
//	virtual void fax(std::vector<Document*>docs) = 0;
//};
//
//struct MFP : IMachine {
//	virtual void print(std::vector<Document*> docs) override
//	{
//	}
//	virtual void scan(std::vector<Document*> docs) override
//	{
//	}
//	virtual void fax(std::vector<Document*> docs) override
//	{
//	}
//};

struct IPinter {
	virtual void print(std::vector<Document*> docs) = 0;
};

struct IScanner {
	virtual void scan(std::vector<Document*> docs) = 0;
};

struct Printer : IPinter {
	virtual void print(std::vector<Document*> docs) override
	{
	}
};

struct Scanner : IScanner {
	virtual void scan(std::vector<Document*> docs) override
	{
	}
};

struct IMachine : IPinter, IScanner {};

struct Machine : IMachine {
	IPinter& printer;
	IScanner& scanner;

	Machine(IPinter& printer, IScanner& scanner) :
		printer{ printer }, scanner{ scanner } 
	{
	}

	virtual void print(std::vector<Document*> docs) override
	{
		printer.print(docs);
	}
	virtual void scan(std::vector<Document*> docs) override
	{
		scanner.scan(docs);
	}
};

 

// Example.5 Dependency Injection
#include <iostream>
#include <memory>
//#include "di.hpp"

struct ILogger {
	virtual ~ILogger() {}
	virtual void Log(std::string const& s) = 0;
};

struct ConsoleLogger : ILogger
{
	void Log(std::string const& s) override {
		std::cout << "LOG: " << s.c_str() << std::endl;
	}
};

struct Engine {
	float volume = 5;
	int horse_power = 400;
	friend std::ostream& operator<<(std::ostream& os, Engine const& obj) {
		return os << "volume : " << obj.volume << " horse_power : " << obj.horse_power;
	}
};

struct Car{
	std::shared_ptr<Engine> engine;
	std::shared_ptr<ILogger> logger;

	// Case.1 명시적 생성자를 이용하기
	explicit Car(std::shared_ptr<Engine> const& engine) :
		engine{ engine } {}

	// Case.2 boost 라이브러리를 이용하기
	explicit Car(std::shared_ptr<Engine> const& engine, std::shared_ptr<ILogger> const& i_logger) :
		engine{ engine },
		logger {i_logger}
	{
		logger->Log("Created a car");
	}

	friend std::ostream& operator<<(std::ostream& os, Car const& obj) {
		return os << "engine : " << *obj.engine;
	}
};

auto main() ->int {
	// Case.1 명시적 생성자를 이용하기
	auto e = std::make_shared<Engine>();
	auto c = std::make_shared<Car>(e);

	// Case.2 boost 라이브러리를 이용하기
	//using namespace boost;
	//auto injector = di::make_injector( 
	//	di::bind<ILogger>().to<ConsoleLogger>()	
	//);
	//auto c = injector.create<std::shared_ptr<Car>>();

	std::cout << *c << std::endl;

	return 0;
}

'Design Pattern' 카테고리의 다른 글

Singleton 싱글턴  (0) 2022.10.25
ProtoType Factory  (0) 2022.03.08
Builder  (0) 2020.11.30
복사했습니다!