The Open-Closed Principle
1.OOP // Open-Closed Principle
객체 지향 디자인에 대한 일반적 지식들이 항상 옳을까?
All member variables should be private. 모든 멤버 변수들은 private 되어야만 한다.
Global variables should be avoided. 전역 변수는 피해야만 한다.
Using run time type identification is dangerous. RTTI는 위험하다.
모든 시스템은 그들의 생명 주기 동안 변하며, 오래 지속될 것을 예상하여 시스템 초기 개발에 염두 하여야 한다.
"SOFTWARE ENTITIES(CLASSES, MODULES, FUNCTIONS, ETC.)
SHOULD BE OPEN FOR EXTENSION, BUT CLOSED FOR MODIFICATION."
"소프트웨어 주요구성요소들은 확장에는 개방되어야 하지만, 변형에는 닫혀있어야 한다."
프로그램은 일반적으로 변형되기 쉽고, 외부 영향을 받으며 예측하기 어렵고, 재사용이 어렵다.
Open-Closed Principle 의 2가지 요소
1. Open for Extension 확장을 위한 개방
2. Closed for Modification 변동을 막는 패쇄
일반적으로 모듈의 동작을 확장하려면 모듈의 변화가 야기된다.
어떻게 두 가지 상반되는 요소를 풀수 있을까?
Abstraction is key
Abstraction 추상적 개념을 도입하면 구현이 가능하다.
Abstraction이란 Base 클래스를 추상화 하고 모든 상속 받는 클래스들에게 나타내어지는 경계선 없는 그룹이다.
Abstraction은 변형에 닫혀 있을 수 있고, 새로운 파생을 상속 하게 함으로 확장에는 열릴 수 있다.
OOD solution to Square/Circle problem. // open-closed principle
class Shape
{
public:
virtual void Draw() const = 0;
};
class Square : public Shape
{
public:
virtual void Draw() const;
};
class Circle : public Shape
{
public:
virtual void Draw() const;
};
void DrawAllShapes(Set<Shape*>& list)
{
for (Iterator<Shape*>i(list); i; i++)
(*i)->Draw();
}
2.Advanced Issue DrawAllShapes with Ordering Issue
DrawAllShapes with Ordering
Shape with ordering methods.
class Shape
{
public:
virtual void Draw() const = 0;
virtual bool Precedes(const Shape&) const = 0;
bool operator<(const Shape& s) {return Precedes(s);}
};
void DrawAllShapes(Set<Shape*>& list)
{
// copy elements into OrderedSet and then sort.
OrderedSet<Shape*> orderedList = list;
orderedList.Sort();
for (Iterator<Shape*> i(orderedList); i; i++)
(*i)->Draw();
}
Ordering a Circle
bool Circle::Precedes(const Shape& s) const
{
if (dynamic_cast<Square*>(s))
return true;
else
return false;
}
DrawAllShapes with Ordering - ocp
#include <typeinfo.h>
#include <string.h>
enum {false, true};
typedef int bool;
class Shape
{
public:
virtual void Draw() const = 0;
virtual bool Precedes(const Shape&) const;
bool operator<(const Shape& s) const
{
return Precedes(s);
}
private:
static char* typeOrderTable[];
};
char* Shape::typeOrderTable[] = { "Circle", "Square", 0 };
// This function searches a table for the class names.
// The table defines the order in which the
// shapes are to be drawn. Shapes that are not
// found always precede shapes that are found.
bool Shape::Precedes(const Shape& s) const
{
const char* thisType = typeid(*this).name();
const char* argType = typeid(s).name();
bool done = false;
int thisOrd = -1;
int argOrd = -1;
for (int i=0; !done; i++)
{
const char* tableEntry = typeOrderTable[i];
if (tableEntry != 0)
{
if (strcmp(tableEntry, thisType) == 0)
thisOrd = i;
if (strcmp(tableEntry, argType) == 0)
argOrd = i;
if ((argOrd > 0) && (thisOrd > 0))
done = true;
}
else // table entry == 0
{
done = true;
}
}
return thisOrd < argOrd;
}
3.Advanced Issue RTTI is Dangerous.
참고 1.
RTTI violating the open-closed principle.
class Shape {};
class Square : public Shape
{
private:
Point itsTopLeft;
double itsSide;
friend DrawSquare(Square*);
};
class Circle : public Shape
{
private:
Point itsCenter;
double itsRadius;
friend DrawCircle(Circle*);
};
void DrawAllShapes(Set<Shape*>& ss)
{
for (Iterator<Shape*>i(ss); i; i++)
{
Circle* c = dynamic_cast<Circle*>(*i); // violating open-closed principle
Square* s = dynamic_cast<Square*>(*i);
if (c)
DrawCircle(c);
else if (s)
DrawSquare(s);
}
}
The difference between these two is that the first, Listing 9, must be changed whenever a new type of Shape is derived.
Listing 10
RTTI that does not violate the open-closed Principle.
class Shape
{
public:
virtual void Draw() cont = 0;
};
class Square : public Shape
{
// as expected.
};
void DrawSquaresOnly(Set<Shape*>& ss)
{
for (Iterator<Shape*>i(ss); i; i++)
{
Square* s = dynamic_cast<Square*>(*i);
if (s)
s->Draw(); // not violating open-closed principle
}
}
if a use of RTTI does not violate the open-closed principle, it is safe.
'Programming > Design' 카테고리의 다른 글
SRP : The Single Responsibility Principle (0) | 2011.07.18 |
---|---|
상속클래스에서 재정의 되는 인터페이스 메서드 (0) | 2011.07.18 |