标签:需要 模式 circle imm mob 导致 结构 更改 避免
敏捷设计是一个过程,它是一个持续的应用原则、模式以及实践来改进软件的结构和可读性,避免软件腐化的过程。
就一个类而言,应该仅有一个引起它变化的原因。在SRP中,我们把职责定义为“变化的原因”。如果你能够想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责。当需求发生改变时,该变化会反映为类的职责的变化。如果一个类承担了多于一个的职责,那么对引起变化的职责的修改,有可能会影响到其他职责的功能。
软件实体(类、模块、函数等等)应该是可以扩展的,但不可以修改,它们对于扩展是开放的,对于更改是封闭的。当需求改变时,不应该修改已有的模块的源代码和二进制代码,而是通过扩展模块的功能来满足需求的变更。
实现OCP原则的关键工具是抽象。对于行为容易发生改变的部分,利用抽象体进行连接。模块之间在行为容易改变的地方通过相对固定的抽象体相互连接,利用抽象的多态性实现行为的扩展。
两个例子:
通过抽象体Drawable实现绘制形状,当有新的形状时,paint函数和已有的shape都不需要修改(封闭),只需要增加(扩展)新的形状类。
class Drawable
{
public:
virtual void Draw() = 0;
}
void Window::paint()
{
for (Drawable *s : shapes_)
{
s->Draw();
}
}
class Rectangle : public Drawable
{
public:
void Draw()
{
// draw Rectangle
}
}
class Circle : public Drawable
{
public:
void Draw()
{
// draw Circle
}
}
为了使得上面例子的paint函数支持绘制形状的顺序性,同时使得顺序性的变化具有可扩展性,需要将顺序变化部分抽象出来以供扩展(开放),并将按照顺序画形状这一行为固化(封闭)。
class Drawable
{
public:
virtual void Draw() = 0;
bool operator<(const Drawable &) const = 0;
}
void Window::paint()
{
vector<Drawable*> ordered_list = shapes_;
sort(ordered_list.begin(), ordered_list.end());
for (Drawable *s : ordered_list)
{
s->Draw();
}
}
class Rectangle : public Drawable
{
public:
void Draw()
{
// draw Rectangle
}
bool operator<(const Drawable &s) const
{
// order
}
}
class Circle : public Drawable
{
public:
void Draw()
{
// draw Circle
}
bool operator<(const Drawable &s) const
{
// order
}
}
子类型必须能够替换掉它们的基类型。Liskov原则是使得OCP成为可能的主要原则和保证。子类型对于基类性的替代是一种IS-A的关系,而IS-A并不是概念上的,而应该是行为上的,不能说正方形属于矩形,就一定要让正方形继承自矩形。行为方式才是软件真正关注的问题。
这种行为上的一致,可以用基于契约的设计来指导,比如每个方法对于前置条件、后置条件的约束在子类中都需要得到保证。基于契约的设计常常通过单元测试来检测和实现。
在层次化的构架设计中,应该由高层次的模块拥有抽象接口,低层次模块实现该抽象接口。这样高层次模块并不知道低层次模块的细节,只是通过抽象接口向低层次模块提出了服务的需求。这和通常的想法有些不同,通常是由低层次的模块设计和拥有对外接口。不反对低层次模块具有调用接口,但是在层次构架中,低层次的模块需要根据高层次模块提出的接口需要,组合自己的功能,实现高层次模块的接口需求。
同时在高层次模块中不应该保存具体的低层次模块的指针或引用,而是应该保存抽象接口的指针或引用。
不应该强迫客户依赖于他们不用的方法。使用委托或是多重继承等方法,将接口进行分离。当客户在使用一个类时,如果使用的是该类的一个职责,就只将该职责对应的接口提供给客户。如果强迫客户程序依赖于他们不使用的方法,那么这些客户就面临着由于这些未使用方法的改变所带来的变更。
标签:需要 模式 circle imm mob 导致 结构 更改 避免
原文地址:https://www.cnblogs.com/songyuncen/p/12076142.html