Pattern_SOLID - 8BitsCoding/RobotMentor GitHub Wiki
- Single Responsibility Principle : ๋ชจ๋ ํด๋์ค๋ ํ๋์ ์ฑ ์์ ๊ฐ์ ธ์ผํ๋ค.
- Open-Closed Principle : ๊ธฐ์กด ์ฝ๋๋ฅผ ๋ณ๊ฒฝํ์ง ์์ผ๋ฉด์ ๊ธฐ๋ฅ์ ์ถ๊ฐํ ์ ์์ด์ผํ๋ค.
- Liskov Substituation(๋ฆฌ์ค์ฝํ ์์ธ์์ด์ ) Principle : ์๋ชป๋ ์์์ ์
- Interface Segregation Principle : ์ธํฐํ์ด์ค ๋ถ๋ฆฌ ์์น, ํด๋ผ์ด์ธํธ๊ฐ ์ฌ์ฉํ์ง ์๋ ๋ฉ์๋์ ์์กดํ์ง ์์์ผํ๋ค.
- Dependency Inversion Principle :
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <boost/lexical_cast.hpp>
using namespace std;
struct Journal
{
string title;
vector<string> entries;
explicit Journal(const string& title)
: title{title}
{
}
void add(const string& entry);
// persistence is a separate concern
void save(const string& filename);
};
void Journal::add(const string& entry)
{
static int count = 1;
entries.push_back(boost::lexical_cast<string>(count++)
+ ": " + entry);
}
void Journal::save(const string& filename)
{
ofstream ofs(filename);
for (auto& s : entries)
ofs << s << endl;
}
struct PersistenceManager
{
static void save(const Journal& j, const string& filename)
{
ofstream ofs(filename);
for (auto& s : j.entries)
ofs << s << endl;
}
};
void main()
{
Journal journal{"Dear Diary"};
journal.add("I ate a bug");
journal.add("I cried today");
//journal.save("diary.txt");
PersistenceManager pm;
pm.save(journal, "diary.txt");
}
Journal์๋ Journal๋ง์ ๊ธฐ๋ฅ์ ๋ด๊ณ ์์ด์ผํ๋ค.
ProductFilter๋ฅผ ์ถ๊ฐํ๊ณ ์ถ๋ค๋ฉด, ๋งค๋ฒ by_color, by_size, by_size_and_color .... ํจ์๋ฅผ ์๋ก ์ถ๊ฐํ๋ฉด์ ๋ง๋ค์ด์ผํ๋??
// open closed principle
// open for extension, closed for modification
#include <string>
#include <vector>
#include <iostream>
using namespace std;
enum class Color { red, green, blue };
enum class Size { small, medium, large };
struct Product
{
string name;
Color color;
Size size;
};
struct ProductFilter
{
typedef vector<Product*> Items;
Items by_color(Items items, const Color color)
{
Items result;
for (auto& i : items)
if (i->color == color)
result.push_back(i);
return result;
}
Items by_size(Items items, const Size size)
{
Items result;
for (auto& i : items)
if (i->size == size)
result.push_back(i);
return result;
}
Items by_size_and_color(Items items, const Size size, const Color color)
{
Items result;
for (auto& i : items)
if (i->size == size && i->color == color)
result.push_back(i);
return result;
}
};
Filter, Spec์ ๋๋์ด์ ๊ด๋ฆฌํด๋ณด์.
template <typename T> struct AndSpecification;
template <typename T> struct Specification
{
virtual ~Specification() = default;
virtual bool is_satisfied(T* item) const = 0;
// new: breaks OCP if you add it post-hoc
/*AndSpecification<T> operator&&(Specification<T>&& other)
{
return AndSpecification<T>(*this, other);
}*/
};
// new:
template <typename T> AndSpecification<T> operator&&
(const Specification<T>& first, const Specification<T>& second)
{
return { first, second };
}
struct ColorSpecification : Specification<Product>
{
Color color;
ColorSpecification(Color color) : color(color) {}
bool is_satisfied(Product *item) const override {
return item->color == color;
}
};
struct SizeSpecification : Specification<Product>
{
Size size;
explicit SizeSpecification(const Size size)
: size{ size }
{
}
bool is_satisfied(Product* item) const override {
return item->size == size;
}
};
template <typename T> struct AndSpecification : Specification<T>
{
const Specification<T>& first;
const Specification<T>& second;
AndSpecification(const Specification<T>& first, const Specification<T>& second)
: first(first), second(second) {}
bool is_satisfied(T *item) const override {
return first.is_satisfied(item) && second.is_satisfied(item);
}
};
์ฌ๊ธฐ๊น์ง Spec๊ด๋ฆฌ
template <typename T> struct Filter
{
virtual vector<T*> filter(vector<T*> items,
Specification<T>& spec) = 0;
};
struct BetterFilter : Filter<Product>
{
vector<Product*> filter(vector<Product*> items,
Specification<Product> &spec) override
{
vector<Product*> result;
for (auto& p : items)
if (spec.is_satisfied(p))
result.push_back(p);
return result;
}
};
์ฌ๊ธฐ๊น์ง Filter๊ด๋ฆฌ
์? ๊ทผ๋ฐ ์์ค์ฝ๋๊ฐ ํจ~~~์ฌ ๋ ์ด๋ ค์์ง๊ฑฐ ๊ฐ์๋ฐ??
main๋ฌธ์ ๋ด์ผํ๋ค. ์ค์ ์ฝ๋๋ฅผ ์ฌ์ฉํ๋ ์ ์ฅ์์๋ ํจ์ฌ ์ฌ์์ก์.
int main()
{
Product apple{"Apple", Color::green, Size::small};
Product tree{"Tree", Color::green, Size::large};
Product house{"House", Color::blue, Size::large};
const vector<Product*> all { &apple, &tree, &house };
BetterFilter bf;
ColorSpecification green(Color::green);
auto green_things = bf.filter(all, green);
for (auto& x : green_things)
cout << x->name << " is green\n";
SizeSpecification large(Size::large);
AndSpecification<Product> green_and_large(green, large);
//auto big_green_things = bf.filter(all, green_and_large);
// use the operator instead (same for || etc.)
auto spec = green && large;
for (auto& x : bf.filter(all, spec))
cout << x->name << " is green and large\n";
// warning: the following will compile but will NOT work
// auto spec2 = SizeSpecification{Size::large} &&
// ColorSpecification{Color::blue};
getchar();
return 0;
}
๊ธฐ์กด์ ์ฝ๋๋ฅผ ๋ณ๊ฒฝํ์ง ์๊ณ ์ ๊ธฐ๋ฅ์ ์ถ๊ฐํ ์์์์ ํ์ธํ๋ค.
// Objects in a program should be replaceable with instances of their subtypes
// w/o altering the correctness of the program
#include <iostream>
class Rectangle
{
protected:
int width, height;
public:
Rectangle(const int width, const int height)
: width{width}, height{height} { }
int get_width() const { return width; }
virtual void set_width(const int width) { this->width = width; }
int get_height() const { return height; }
virtual void set_height(const int height) { this->height = height; }
int area() const { return width * height; }
};
class Square : public Rectangle
{
public:
Square(int size): Rectangle(size,size) {}
void set_width(const int width) override {
this->width = height = width;
}
void set_height(const int height) override {
this->height = width = height;
}
};
void process(Rectangle& r)
{
int w = r.get_width();
r.set_height(10);
std::cout << "expected area = " << (w * 10)
<< ", got " << r.area() << std::endl;
}
int main342342()
{
Rectangle r{ 3, 4 };
process(r);
// expected area = 30, got 30
Square s{ 5 };
process(s);
// expected area = 50, got 100
getchar();
return 0;
}
์์์ ํตํด ์๋ชป๋ ๊ฒฐ๊ณผ๊ฐ ๋์ถ๋๋ค.
์ด๋ด๊ฒฝ์ฐ ๊ณผ์ฐ ์ ๋๋ก๋ ์์์ด๋ผ ํ ์ ์์๊น?
#include <vector>
struct Document;
//struct IMachine
//{
// virtual void print(Document& doc) = 0;
// virtual void fax(Document& doc) = 0;
// virtual void scan(Document& doc) = 0;
//};
//
//struct MFP : IMachine
//{
// void print(Document& doc) override;
// void fax(Document& doc) override;
// void scan(Document& doc) override;
//};
// 1. Recompile
// 2. Client does not need this
// 3. Forcing implementors to implement too much
struct IPrinter
{
virtual void print(Document& doc) = 0;
};
struct IScanner
{
virtual void scan(Document& doc) = 0;
};
struct Printer : IPrinter
{
void print(Document& doc) override { // do something };
};
struct Scanner : IScanner
{
void scan(Document& doc) override { // do something };
};
struct IMachine: IPrinter, IScanner
{
};
struct Machine : IMachine
{
IPrinter& printer;
IScanner& scanner;
Machine(IPrinter& printer, IScanner& scanner)
: printer{printer},
scanner{scanner}
{
}
void print(Document& doc) override {
printer.print(doc);
}
void scan(Document& doc) override; // do nothing
};
// IPrinter --> Printer
// everything --> Machine
#include <iostream>
#include <string>
#include <vector>
#include <tuple>
using namespace std;
// A. High-level modules should not depend on low-level modules.
// Both should depend on abstractions.
// B. Abstractions should not depend on details.
// Details should depend on abstractions.
enum class Relationship
{
parent,
child,
sibling
};
struct Person
{
string name;
};
struct RelationshipBrowser
{
virtual vector<Person> find_all_children_of(const string& name) = 0;
};
struct Relationships : RelationshipBrowser // low-level
{
vector<tuple<Person, Relationship, Person>> relations;
void add_parent_and_child(const Person& parent, const Person& child)
{
relations.push_back({parent, Relationship::parent, child});
relations.push_back({child, Relationship::child, parent});
}
vector<Person> find_all_children_of(const string &name) override
{
vector<Person> result;
for (auto&& [first, rel, second] : relations)
{
if (first.name == name && rel == Relationship::parent)
{
result.push_back(second);
}
}
return result;
}
};
struct Research // high-level
{
Research(RelationshipBrowser& browser)
{
for (auto& child : browser.find_all_children_of("John"))
{
cout << "John has a child called " << child.name << endl;
}
}
// Research(const Relationships& relationships)
// {
// auto& relations = relationships.relations;
// for (auto&& [first, rel, second] : relations)
// {
// if (first.name == "John" && rel == Relationship::parent)
// {
// cout << "John has a child called " << second.name << endl;
// }
// }
// }
};
Research์์ relations๋ฅผ ์ง์ ์ฌ์ฉํจ์ผ๋ก์ ๋ฌธ์ ๊ฐ ๋ฐ์
๋ง์ฝ relationships.relations์์๊ฐ ๋ณ๊ฒฝ๋๊ฒ ๋๋ค๋ฉด high-level์์ฒด์ ์ฝ๋๋ฅผ ๋ค ์์ ํด์ผํ๋ค.
์ด๋ป๊ฒ ์์ ์์ด ํ ๋ฐฉ๋ฒ์? -> find_all_children_ofํจ์์์ฑ
int main()
{
Person parent{"John"};
Person child1{"Chris"};
Person child2{"Matt"};
Relationships relationships;
relationships.add_parent_and_child(parent, child1);
relationships.add_parent_and_child(parent, child2);
Research _(relationships);
return 0;
}