
- 추상 클래스를 상속받았지만, 베이스 클래스에 사용하지 않는 함수임에도, 구현해야 한다면, 어떤 원칙이 위배되었는가?
- 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.
- 접근이 용이한가?
- Single Responsibility Principle(SRP) - 단일 책임 원칙
//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 |