软件设计的五大原则.ppt
《软件设计的五大原则.ppt》由会员分享,可在线阅读,更多相关《软件设计的五大原则.ppt(51页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、软件设计中的软件设计中的5大原则大原则1.单一职责原则(单一职责原则(SRP)陈述:陈述:就一个类而言,应该只有一个导致其变化的原因就一个类而言,应该只有一个导致其变化的原因分析:分析:一个职责就是一个变化的轴线一个职责就是一个变化的轴线一个类如果承担的职责过多,就等于将这些职责耦合在一个类如果承担的职责过多,就等于将这些职责耦合在一起。一个职责的变化可能会虚弱或者抑止这个类完成一起。一个职责的变化可能会虚弱或者抑止这个类完成其它职责的能力其它职责的能力多职责将导致多职责将导致脆弱脆弱性的臭味性的臭味Rectangle+draw()+area():doubleComputational Geo
2、metry ApplicationGraphical ApplicationGUIRectangleRectangle类具有两个职责:类具有两个职责:1.1.计算矩形面积的数学模型计算矩形面积的数学模型2.2.将矩形在一个图形设备上描述出来将矩形在一个图形设备上描述出来示例示例1 1:RectangleRectangle类违反了类违反了SRPSRP,具有两个职能,具有两个职能计算面积和计算面积和绘制矩形绘制矩形这种对这种对SRPSRP的违反将导致两个方面的问题:的违反将导致两个方面的问题:包含不必要的代码包含不必要的代码一个应用可能希望使用一个应用可能希望使用RetangleRetangle类
3、计算矩形的面积,但是却被迫类计算矩形的面积,但是却被迫将绘制矩形相关的代码也包含进来将绘制矩形相关的代码也包含进来一些逻辑上毫无关联的原因可能导致应用失败一些逻辑上毫无关联的原因可能导致应用失败如果如果GraphicalApplicationGraphicalApplication的需求发生了变化,从而对的需求发生了变化,从而对RectangleRectangle类进行了修改。但是这样的变化居然会要求我们重新构建、测试类进行了修改。但是这样的变化居然会要求我们重新构建、测试以及部署以及部署ComputationalGeometryApplicationComputationalGeometry
4、Application,否则其将莫名其,否则其将莫名其妙的失败。妙的失败。修改后的设计如下:修改后的设计如下:ModemModem类(可能)有两个职责:类(可能)有两个职责:1.1.拨号拨号2.2.通信通信示例示例2 2:一个一个ModemModem的接口:的接口:Class ModemClass Modempublic:public:virtual void dail(char*pno)=0;virtual void dail(char*pno)=0;virtual void hangup()=0;virtual void hangup()=0;virtual void send(char
5、c)=0;virtual void send(char c)=0;virtual void recv()=0;virtual void recv()=0;什么是职责?什么是职责?职责是职责是“变化的原因变化的原因”。上面的例子可能存在两种变化的方式:上面的例子可能存在两种变化的方式:连接和通信可能独立变化连接和通信可能独立变化在这种情况下,应该将职责分开。例如,应用的变化导致了连接部分在这种情况下,应该将职责分开。例如,应用的变化导致了连接部分方法的签名(方法的签名(signaturesignature)发生了变化,那么使用数据连接的部分也需)发生了变化,那么使用数据连接的部分也需要重新编译、
6、部署,这会相当麻烦,使得设计要重新编译、部署,这会相当麻烦,使得设计僵化僵化。连接和通信同时变化连接和通信同时变化这种情况下,不必将职责分开。反而分离可能导致这种情况下,不必将职责分开。反而分离可能导致“不必要的复杂性不必要的复杂性”的臭味的臭味刻舟求剑是错误的。刻舟求剑是错误的。王亚沙王亚沙修改后的设计如下:修改后的设计如下:有一点需要注意:在有一点需要注意:在ModemImplementationModemImplementation中中实际还是集合了两个职实际还是集合了两个职责。这是我们不希望的,责。这是我们不希望的,但是有时候却是必须的。但是有时候却是必须的。但是我们注意到,对于但是我
7、们注意到,对于应用的其它部分,通过应用的其它部分,通过接口的分离我们已经实接口的分离我们已经实现了职责的分离。现了职责的分离。ModemImplementationModemImplementation已已经不被其它任何程序所经不被其它任何程序所依赖。除了依赖。除了mainmain以外,以外,其他所有程序都不需要其他所有程序都不需要知道这个函数的存在。知道这个函数的存在。常见错误提醒:常见错误提醒:持久化与业务规则的耦合。持久化与业务规则的耦合。例如:例如:业务规则经常变化,而业务规则经常变化,而持久化方法却一般不变。持久化方法却一般不变。将这两个职责耦合在一将这两个职责耦合在一起,将导致每次
8、因为业起,将导致每次因为业务规则变化调整务规则变化调整EmployeeEmployee类时,所有持类时,所有持久化部分的代码也要跟久化部分的代码也要跟着变化着变化 2.开放封闭原则(开放封闭原则(OCP)陈述:陈述:软件实体(类、模块、函数等)应该是可以扩展的,同时还可以是软件实体(类、模块、函数等)应该是可以扩展的,同时还可以是不必修改的,更确切的说,函数实体应该:不必修改的,更确切的说,函数实体应该:(1 1)对扩展是开放的)对扩展是开放的当应用的需求变化时,我们可以对模块进行扩展,使其具有满足改当应用的需求变化时,我们可以对模块进行扩展,使其具有满足改变的新的行为变的新的行为即,我们可以
9、改变模块的功能即,我们可以改变模块的功能(2 2)对更改是封闭的)对更改是封闭的对模块进行扩展时,不必改动已有的源代码或二进制代码。对模块进行扩展时,不必改动已有的源代码或二进制代码。分析:分析:世界是变化的(而且变化很快),软件是对现实的抽象世界是变化的(而且变化很快),软件是对现实的抽象软件必须能够扩展软件必须能够扩展如果任何修改都需要改变已经存在的代码,那么可能导致牵一发动如果任何修改都需要改变已经存在的代码,那么可能导致牵一发动全身现象,进而导致雪崩效应,使软件质量显著下降全身现象,进而导致雪崩效应,使软件质量显著下降实现实现OCPOCP的关键是抽象:的关键是抽象:例子例子1 1cla
10、ss clientserver&s;public:client(server&SER):s(SER)void useServer()s.ServerFunc();class serverint serverData;public:void ServerFunc();例子例子1 1(续)(续)这个程序出了什么问题?这个程序出了什么问题?clientclient和和serverserver都是具体类,接口与实现没有实现分离。如果我们都是具体类,接口与实现没有实现分离。如果我们想要让想要让clientclient调用一个新的调用一个新的serverserver类,那么我们不得不修改类,那么我们不得不
11、修改clientclient的源代码的源代码从而带来编译、链接、部署等一系列的问题。从而带来编译、链接、部署等一系列的问题。见下页程序2.开放封闭原则(开放封闭原则(OCP)例子例子1 1(续)(续)class clientserver&s;public:client(server&SER):s(SER)void useServer()s.ServerFunc();class serverint serverData;public:void ServerFunc();class server1int serverData;public:void ServerFunc();class clien
12、tserver1&s;public:client(server&SER):s(SER)void useServer()s.ServerFunc();2.开放封闭原则(开放封闭原则(OCP)例子例子1 1(续)(续)修改后的设计修改后的设计class clientClientInterface&ci;public:client(ClientInterface&CI):ci(CI)void useServer()ci.ServerFunc();class ClientInterfacevirtual void ServerFunc()=0;class server:public ClientInt
13、erfaceint serverData;public:void ServerFunc();例子例子1 1(续)(续)一个问题:一个问题:为什么上述的为什么上述的ClientInterfaceClientInterface这个类要取这么个名字,叫做这个类要取这么个名字,叫做AbastractServerAbastractServer岂不更好?岂不更好?其实这里面蕴含了一个思想:其实这里面蕴含了一个思想:clientclient类中更多的描述了高层的策略,而类中更多的描述了高层的策略,而ServerServer类中是对这些策略的一种类中是对这些策略的一种具体实现。具体实现。而接口是策略的一个组成
14、部分,他根而接口是策略的一个组成部分,他根clientclient端的关系更加密切端的关系更加密切我们应该这样想问题:我们应该这样想问题:ClientInterfaceClientInterface中定义了中定义了clientclient期期 望望ServerServer做什么,而做什么,而serverserver具体类是对具体类是对clientclient这种要求的这种要求的 一种具体实现。一种具体实现。OCPOCP原则其实是要求我们清晰的区分策略和策略的具体实现形式。允许原则其实是要求我们清晰的区分策略和策略的具体实现形式。允许 扩展具体的实现形式(开放),同时将这种扩展与策略隔离开来,使
15、扩展具体的实现形式(开放),同时将这种扩展与策略隔离开来,使 其对上层的策略透明(封闭)。其对上层的策略透明(封闭)。例子例子2 2C C语言程序语言程序-shape.h-emum ShapeTypecircle,square;struct ShapeShapeType itsType;-circle.h-struct CircleShapeType itsType;double itsRadius;CPoint itscenter;-square.h-struct SquareShapeType itsType;double itsSide;CPoint itsTopLeft;-drawAl
16、lShapes.cpp-typedef struct Shape*ShapePointer;void DrawAllShapes(ShapePointer list,int n)int i;for(i=0;iitsType)case square:s-Square();break;case circle:s-DrawCircle();break;例子例子2 2(续)(续)批判批判这个程序不符合这个程序不符合OCPOCP,如果需要处理的几何图形中再加入,如果需要处理的几何图形中再加入“三角形三角形”将将引发大量的修改引发大量的修改僵化的僵化的增加增加TriangleTriangle会导致会导致S
17、hapeShape、SquareSquare、CircleCircle以及以及DrawAllShapesDrawAllShapes的重新编的重新编译和部署译和部署脆弱的脆弱的因为存在大量的既难以查找又难以理解的因为存在大量的既难以查找又难以理解的SwitchSwitch和和IfIf语句,修改稍有不慎,语句,修改稍有不慎,程序就会莫明其妙的出错程序就会莫明其妙的出错牢固的牢固的想在一个程序中复用想在一个程序中复用DrawAllShapesDrawAllShapes,都必须带上,都必须带上CircleCircle、SquareSquare,即使,即使那个程序不需要他们那个程序不需要他们例子例子2
18、2(续)(续)修改后的设计修改后的设计class Shapepublic:virtual void Draw()const=0;class Square:public Shapepublic:virtual void Draw()const;class Circle:public Shapepublic:virtual void Draw()const;void DrawAllShapes(Vector&list)vector:iterator i;for(i=list.begin();i!=list.end();i+)(*i)-Draw();例子例子2 2(续)(续)再看这些批判再看这些批判
19、再加入再加入“三角形三角形”将变得十分简单:将变得十分简单:僵化的僵化的增加增加TriangleTriangle会导致会导致ShapeShape、SquareSquare、CircleCircle以及以及DrawAllShapesDrawAllShapes的重新编的重新编译和部署译和部署脆弱的脆弱的因为存在大量的既难以查找又难以理解的因为存在大量的既难以查找又难以理解的SwitchSwitch和和IfIf语句,修改稍有不慎,语句,修改稍有不慎,程序就会莫明其妙的出错程序就会莫明其妙的出错牢固的牢固的想在一个程序中复用想在一个程序中复用DrawAllShapesDrawAllShapes,都必须
20、带上,都必须带上CircleCircle、SquareSquare,即使,即使那个程序不需要他们那个程序不需要他们谎言:谎言:上述代码并不完全封闭上述代码并不完全封闭“如果我们希望正方形在所有圆之前绘如果我们希望正方形在所有圆之前绘制制”会怎么样?会怎么样?对绘图的顺序无法实现封闭对绘图的顺序无法实现封闭更糟糕的是,刚才的设计反而成为了实现更糟糕的是,刚才的设计反而成为了实现“正方形在所正方形在所有圆之前绘制有圆之前绘制”功能的障碍。功能的障碍。真实的谎言:真实的谎言:一般而言,无论模块多么一般而言,无论模块多么“封闭封闭”,都会存在一些无法对之封闭的,都会存在一些无法对之封闭的变化变化没有对
21、所有变化的情况都封闭的模型没有对所有变化的情况都封闭的模型我们怎么办?我们怎么办?既然不可能完全封闭,我们必须有策略的对待此问题既然不可能完全封闭,我们必须有策略的对待此问题对模型应该对模型应该封闭那类变化作出选择,封闭最可能出现的变化封闭那类变化作出选择,封闭最可能出现的变化 这需要对领域的了解,丰富的经验和常识这需要对领域的了解,丰富的经验和常识错误的判断反而不美,因为错误的判断反而不美,因为OCPOCP需要额外的开销需要额外的开销(增加复杂度)(增加复杂度)敏捷的思想敏捷的思想我们预测他们,但是直到我们发现他们才行动我们预测他们,但是直到我们发现他们才行动回到例回到例2 2:要实现对排序
22、的封闭应该如何设计?要实现对排序的封闭应该如何设计?class Shapeclass Shapepublic:public:virtual void Draw()const=0;virtual void Draw()const=0;virtual bool Precedes(const Shape&)const=0;virtual bool Precedes(const Shape&)const=0;bool operatorconst Shape&s)return Precedes(s);bool operatorconst Shape&s)return Precedes(s);templa
23、tetemplateclass Lesspclass Lessppublic:bool operator()const P p,const P q)return(*p)(*q);public:bool operator()const P p,const P q)return(*p)(*q);void DrawAllShapes(vector&list)void DrawAllShapes(vector&list)vector orderedList=list;vector orderedList=list;sort(orderedList.begin(),orderedList.end(),L
24、essp();sort(orderedList.begin(),orderedList.end(),Lessp();vector:const_iterator I;vector:const_iterator I;for(i=orderedList.begin();i!=orderedList.end();i+)for(i=orderedList.begin();i!=orderedList.end();i+)(*i)-Draw();(*i)-Draw();对于各个对于各个ShapeShape的派生类,需要实现具体的排序规则的派生类,需要实现具体的排序规则CircleCircle类的排序规则实现
25、如下:类的排序规则实现如下:Bool Circle:Precedes(const Shape&s)constBool Circle:Precedes(const Shape&s)constif(dynamic_cast(s)if(dynamic_cast(s)return true;return true;elseelsereturn false;return false;这个程序符合这个程序符合OCPOCP吗?吗?利用利用“数据驱动数据驱动”的方法获得封闭性的方法获得封闭性#include#include#include using namespace std;class Shapepubl
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 软件设计 原则
限制150内