软件工程面向对象的设计原则
合集下载
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
public DBManager(string id) {
m_id = id; }
IDBAction Members }
public class DBManagerProxy : IDBAction { private IDBAction dbManager;
public DBManagerProxy(IDBAction dbAction) {
如果用ToggleSwitch控制一台电视就很困难了。总不能让TV继承自Light吧。
任何变量都不应该持有一个指向具体类 的指针或者引用
任何类都不应该从具体类派生(始于抽象, 来自具体)
任何方法都不应该覆写它的任何基类中 的已经实现了的方法
接口隔离原则(ISP)
定义
客户端不应该依赖它不需要的接口 类间的依赖关系应该建立在最小的接口上
根据接口隔离原则拆分接口时,首先必 须满足单一职责原则。
接口要高内聚
高内聚就是要提高接口、类、模块的处理能 力,减少对外的交互。
具体到接口隔离原则就是,要求在接口中尽 量少公布public方法,接口是对外的承诺, 承诺地越少对系统开发越有利,变更的风险 也就越少,同时也有利于降低成本。
定制服务
定制服务就是单独为一个个体提供优良的服务。
接口设计是有限度的
接口的设计粒度越小,系统越灵活,这是不争的事 实。但是,灵活的同时也带来了结构的复杂化,开 发难度增加,可维护性降低,这不是一个项目或产 品所期望看到的,所以接口设计一定要注意适度, 这个度只能根据经验和常识判断,没有一个固化或 可测量的标准
gerProxy(new DBManager("CanAdd")); dbManager.Add();
} }
里氏代换原则(LSP)
一个软件实体如果使用的是一个基类的话那么 一定适用于其子类,而且它察觉不出基类对象 和子类对象的区别。也就是说,在软件里面, 把基类都替换成它的子类,程序的行为没有变 化。
单一职责原则
(SRP: The single responsibility principle )
就一个类而言,应该仅有一个引起它变化 的原因,如果你能想到多于一个的动机去 改变一个类,那么这个类就具有多于一个 的职责.应该把多于的职责分离出去,分别 再创建一些类来完成每一个职责.
系统中的每一个对象都应该只有一个单 独的职责,而任何对象所关注的就是自 身职责的完成
如何做到既不修改,又可以扩展?
抽象化
让模块依赖于一个固定的抽象体,这样 它就是不可以修改的;同时,通过这个 抽象体派生,就可以扩展此模块的行为 功能。这样设计的程序只通过增加代码 来变化而不是通过更改现有代码来变 化。
抽象(Astraction) 多态(Polymorphism) 继承(Inheritance) 接口(Interface)
被无理的实现在一个类中,权限的规则变化和 数据库操作的规则变化,都有可能引起 DBManager修改当前代码。
按照单一职责原则,一个类应该只有一个引起 它改变的原因。所以我们选择以合适的方式来 重构有缺陷的设计,在此显然可以通过实现一 个Proxy模式来解决职责交叉的问题
public class DBManager : IDBAction { private string m_id = string.Empty;
LSP是继承复用的基石,只有当衍生类可以替 换掉基类,软件单位的功能不受到影响时,基 类才能真正被复用,而衍生类也能够在基类的 基础上增加新的行为。
长方形和正方形实例
长方形(A) 正方形(B)
CRectangle Void SetWidth(float _width) CSquare Void SetWidth(float _width)
每当计价策略发生改变, 我们就必须修改Part的每个子类
采用一个PricePolicy类,通过对其进行继 承以提供不同的计价策略;
所做的就是将问题推迟到另一个类中, 将“变化”封装在PricePolicy类里面;
但是使用该解决方案,可通过改变Part对 象,在运行期间动态地来设定计价的策 略。
dbManager = dbAction; }
public string GetPermission(string id) {
return string.Empty; }
IDBAction Members }
public class DBClient { static void Main(string[] args) { IDBAction dbManager = new DBMana
尽量降低一个类的访问权限。 谨慎使用Serializable,一旦将一个类设置成
Serializable,就不能再在新版本中修改这个类 的内部结构,包括private的方法和句段。 尽量降低成员的访问权限。
假设有一类,其中有个method,测试传入的长方形的宽度是 否大于高度,如果满足就停下来,否则就增加宽度的值
void Test(CRectangle _rect) {
float w,h; w = _rect.getWidth(); h = _rect.getHeight(); if ( w > h) return; else _rect.SetWidth(w+deltaw); }
迪米特原则
一个软件实体应当尽可能少的其他实体 发生相互作用。模块之间的交互要少。 这样做的结果是当系统的功能需要扩展 时,会相对更容易地做到对修改的关 闭。 一个对象应当对其他对象有尽可能少的 了解。
迪米特原则的具体操作
优先考虑将一个类设置成不变类。不变类易于 设计、实现和使用。
对象与外界通信大体有两种: 改变该对象的状态和不改变这个对象的状态 慎重设置可变类
依赖倒置原则(DIP)
Dependency Inversion Principle
高层模块不应该依赖于低层模块,二者都应该依赖于 抽象
抽象不应该依赖于细节,细节应该依赖于抽象
高层模块包含了一个应该程序中的重要的策略 选择和业务模型,正是这些高层模块才使得其 所有的应用程序区别于其他,如果高层依赖于 低层,那么对低层模块的改动就会直接影响到高 层模块,从而迫使它们依次做出改动。
一句话:建立单一接口,不要建立臃肿庞大的 接口。再通俗一点讲:接口尽量细化,同时接 口中的Hale Waihona Puke Baidu法尽量少。
提供给每个模块的都应该是单一接口,提供给 几个模块就应该有几个接口,而不是建立一个 庞大的臃肿的接口,容纳所有的客户端访问。
接口要尽量小
这是接口隔离原则的核心定义,不出现 臃肿的接口(Fat Interface),但是“小” 是有限度的,首先就是不能违反单一职 责原则。
OCP实例
以上函数的工作是在制订的电脑部件数组中计 算各个部件价格的总和。
若Part是一个基类或接口且使用了多态,则该
类可很容易地来适应新类型的部件,而不必对
其进行修改。 其将符合OCP
但是在计算总价格时,若财务部颁布主板和内 存应使用额外费用,则将如何去做。
下列的代码是如何来做的呢?
实例
DBManager类对数据库的操作和用户权 限的判别封装在一个类中实现
public void Add() { if (GetPermission(m_id) == CanAdd") { Console.WriteLine("管理员可以增加数据。"); }
}
权限判断的职责、数据库操作的职责
面向对象的设计原则和设计模式
北京化工大学计算机系 袁国栋
面向对象的设计原则
一个好的系统设计应该包括至少三个性质:可 扩展性,和插入性和灵活性。
面向对象设计的原则
开闭原则(基础性原则) 单一职责 里氏代换原则(LSP) 依赖倒致原则(DIP) 接口隔离原则(ISP) 迪米特法则(Law of Demeter LoD)
四边形(C)
正方形(B)
长方形(A)
对于长方形和正方形,取width和height是它们共同的行为,但是给 width和height赋值,两者行为不同,因此,这个抽象的四边形的类 只有取值方法,没有赋值方法。Test方法只会适用于不同的子类, LSP也就不会被破坏。
在进行设计的时候,我们尽量从抽象类继承,而不是从具体类 继承。如果从继承等级树来看,所有叶子节点应当是具体类, 而所有的树枝节点应当是抽象类或者接口。当然这个只是一个 一般性的指导原则,使用的时候还要具体情况具体分析。
开闭原则(Open-Closed Principle OCP )
一个软件实体应该对扩展开放,对修改 关闭。
模块设计时,使得该模块可以在不被修改的前 提下被扩展。 也即就当可以在不必修改源代码的情况下改变 这个模块的行为。 “不能修改而可以扩展” 是不允许更改系统的抽象层,而允许扩展的是 系统的实现层。
缺点:耦合太紧密,Light发生变化将影响ToggleSwitch
怎么办?将Light作成Abstract,然后具体类继承自Light
优点:ToggleSwitch依赖于抽象类 Light,具有更高的稳定性,而 BulbLight与TubeLight继承自 Light,可以根据"开放-封闭"原则 进行扩展。只要Light不发生变化, BulbLight与TubeLight的变化就不 会波及ToggleSwitch。
m_id = id; }
IDBAction Members }
public class DBManagerProxy : IDBAction { private IDBAction dbManager;
public DBManagerProxy(IDBAction dbAction) {
如果用ToggleSwitch控制一台电视就很困难了。总不能让TV继承自Light吧。
任何变量都不应该持有一个指向具体类 的指针或者引用
任何类都不应该从具体类派生(始于抽象, 来自具体)
任何方法都不应该覆写它的任何基类中 的已经实现了的方法
接口隔离原则(ISP)
定义
客户端不应该依赖它不需要的接口 类间的依赖关系应该建立在最小的接口上
根据接口隔离原则拆分接口时,首先必 须满足单一职责原则。
接口要高内聚
高内聚就是要提高接口、类、模块的处理能 力,减少对外的交互。
具体到接口隔离原则就是,要求在接口中尽 量少公布public方法,接口是对外的承诺, 承诺地越少对系统开发越有利,变更的风险 也就越少,同时也有利于降低成本。
定制服务
定制服务就是单独为一个个体提供优良的服务。
接口设计是有限度的
接口的设计粒度越小,系统越灵活,这是不争的事 实。但是,灵活的同时也带来了结构的复杂化,开 发难度增加,可维护性降低,这不是一个项目或产 品所期望看到的,所以接口设计一定要注意适度, 这个度只能根据经验和常识判断,没有一个固化或 可测量的标准
gerProxy(new DBManager("CanAdd")); dbManager.Add();
} }
里氏代换原则(LSP)
一个软件实体如果使用的是一个基类的话那么 一定适用于其子类,而且它察觉不出基类对象 和子类对象的区别。也就是说,在软件里面, 把基类都替换成它的子类,程序的行为没有变 化。
单一职责原则
(SRP: The single responsibility principle )
就一个类而言,应该仅有一个引起它变化 的原因,如果你能想到多于一个的动机去 改变一个类,那么这个类就具有多于一个 的职责.应该把多于的职责分离出去,分别 再创建一些类来完成每一个职责.
系统中的每一个对象都应该只有一个单 独的职责,而任何对象所关注的就是自 身职责的完成
如何做到既不修改,又可以扩展?
抽象化
让模块依赖于一个固定的抽象体,这样 它就是不可以修改的;同时,通过这个 抽象体派生,就可以扩展此模块的行为 功能。这样设计的程序只通过增加代码 来变化而不是通过更改现有代码来变 化。
抽象(Astraction) 多态(Polymorphism) 继承(Inheritance) 接口(Interface)
被无理的实现在一个类中,权限的规则变化和 数据库操作的规则变化,都有可能引起 DBManager修改当前代码。
按照单一职责原则,一个类应该只有一个引起 它改变的原因。所以我们选择以合适的方式来 重构有缺陷的设计,在此显然可以通过实现一 个Proxy模式来解决职责交叉的问题
public class DBManager : IDBAction { private string m_id = string.Empty;
LSP是继承复用的基石,只有当衍生类可以替 换掉基类,软件单位的功能不受到影响时,基 类才能真正被复用,而衍生类也能够在基类的 基础上增加新的行为。
长方形和正方形实例
长方形(A) 正方形(B)
CRectangle Void SetWidth(float _width) CSquare Void SetWidth(float _width)
每当计价策略发生改变, 我们就必须修改Part的每个子类
采用一个PricePolicy类,通过对其进行继 承以提供不同的计价策略;
所做的就是将问题推迟到另一个类中, 将“变化”封装在PricePolicy类里面;
但是使用该解决方案,可通过改变Part对 象,在运行期间动态地来设定计价的策 略。
dbManager = dbAction; }
public string GetPermission(string id) {
return string.Empty; }
IDBAction Members }
public class DBClient { static void Main(string[] args) { IDBAction dbManager = new DBMana
尽量降低一个类的访问权限。 谨慎使用Serializable,一旦将一个类设置成
Serializable,就不能再在新版本中修改这个类 的内部结构,包括private的方法和句段。 尽量降低成员的访问权限。
假设有一类,其中有个method,测试传入的长方形的宽度是 否大于高度,如果满足就停下来,否则就增加宽度的值
void Test(CRectangle _rect) {
float w,h; w = _rect.getWidth(); h = _rect.getHeight(); if ( w > h) return; else _rect.SetWidth(w+deltaw); }
迪米特原则
一个软件实体应当尽可能少的其他实体 发生相互作用。模块之间的交互要少。 这样做的结果是当系统的功能需要扩展 时,会相对更容易地做到对修改的关 闭。 一个对象应当对其他对象有尽可能少的 了解。
迪米特原则的具体操作
优先考虑将一个类设置成不变类。不变类易于 设计、实现和使用。
对象与外界通信大体有两种: 改变该对象的状态和不改变这个对象的状态 慎重设置可变类
依赖倒置原则(DIP)
Dependency Inversion Principle
高层模块不应该依赖于低层模块,二者都应该依赖于 抽象
抽象不应该依赖于细节,细节应该依赖于抽象
高层模块包含了一个应该程序中的重要的策略 选择和业务模型,正是这些高层模块才使得其 所有的应用程序区别于其他,如果高层依赖于 低层,那么对低层模块的改动就会直接影响到高 层模块,从而迫使它们依次做出改动。
一句话:建立单一接口,不要建立臃肿庞大的 接口。再通俗一点讲:接口尽量细化,同时接 口中的Hale Waihona Puke Baidu法尽量少。
提供给每个模块的都应该是单一接口,提供给 几个模块就应该有几个接口,而不是建立一个 庞大的臃肿的接口,容纳所有的客户端访问。
接口要尽量小
这是接口隔离原则的核心定义,不出现 臃肿的接口(Fat Interface),但是“小” 是有限度的,首先就是不能违反单一职 责原则。
OCP实例
以上函数的工作是在制订的电脑部件数组中计 算各个部件价格的总和。
若Part是一个基类或接口且使用了多态,则该
类可很容易地来适应新类型的部件,而不必对
其进行修改。 其将符合OCP
但是在计算总价格时,若财务部颁布主板和内 存应使用额外费用,则将如何去做。
下列的代码是如何来做的呢?
实例
DBManager类对数据库的操作和用户权 限的判别封装在一个类中实现
public void Add() { if (GetPermission(m_id) == CanAdd") { Console.WriteLine("管理员可以增加数据。"); }
}
权限判断的职责、数据库操作的职责
面向对象的设计原则和设计模式
北京化工大学计算机系 袁国栋
面向对象的设计原则
一个好的系统设计应该包括至少三个性质:可 扩展性,和插入性和灵活性。
面向对象设计的原则
开闭原则(基础性原则) 单一职责 里氏代换原则(LSP) 依赖倒致原则(DIP) 接口隔离原则(ISP) 迪米特法则(Law of Demeter LoD)
四边形(C)
正方形(B)
长方形(A)
对于长方形和正方形,取width和height是它们共同的行为,但是给 width和height赋值,两者行为不同,因此,这个抽象的四边形的类 只有取值方法,没有赋值方法。Test方法只会适用于不同的子类, LSP也就不会被破坏。
在进行设计的时候,我们尽量从抽象类继承,而不是从具体类 继承。如果从继承等级树来看,所有叶子节点应当是具体类, 而所有的树枝节点应当是抽象类或者接口。当然这个只是一个 一般性的指导原则,使用的时候还要具体情况具体分析。
开闭原则(Open-Closed Principle OCP )
一个软件实体应该对扩展开放,对修改 关闭。
模块设计时,使得该模块可以在不被修改的前 提下被扩展。 也即就当可以在不必修改源代码的情况下改变 这个模块的行为。 “不能修改而可以扩展” 是不允许更改系统的抽象层,而允许扩展的是 系统的实现层。
缺点:耦合太紧密,Light发生变化将影响ToggleSwitch
怎么办?将Light作成Abstract,然后具体类继承自Light
优点:ToggleSwitch依赖于抽象类 Light,具有更高的稳定性,而 BulbLight与TubeLight继承自 Light,可以根据"开放-封闭"原则 进行扩展。只要Light不发生变化, BulbLight与TubeLight的变化就不 会波及ToggleSwitch。