设计模式学习(一):设计原则

作者:Grey

原文地址:

博客园:设计模式学习(一):设计原则

CSDN:设计模式学习(一):设计原则

开闭原则

对扩展开放,对修改关闭,降低维护带来的新风险

先看一个不符合开闭原则的代码,每次新增一种类型,都需要增加一个if条件判断(修改),如下代码

public class GraphicEditor {public void draw(Shape shape) {if (shape.mType == 1) {drawRectangle();} else if (shape.mType == 2) {drawCircle();}// ... 每次增加一种类型,这里的代码就要加一个if分支。不符合开闭原则}public void drawRectangle() {System.out.println("画长方形");}public void drawCircle() {System.out.println("画圆形");}class Shape {int mType;}class Rectangle extends Shape {Rectangle() {super.mType = 1;}}class Circle extends Shape {Circle() {super.mType = 2;}}
}

要调整为符合开闭原则的代码,需要先将 Shape 抽象为接口,GraphicEditor 中的 draw() 方法中直接传入 Shape 这个接口类型,并调用其 draw() 方法,每次增加一种 Shape 类型,只需要新增一个类,并实现 Shape 接口即可,这样就实现了对扩展开放,而且也不需要修改 GraphicEditor 中的 draw() 方法内容,实现了对修改关闭,修改后的代码如下。

public class GraphicEditor1 {public void draw(Shape shape) {shape.draw();}interface Shape {void draw();}class Rectangle implements Shape {@Overridepublic void draw() {System.out.println("画矩形");}}class Circle implements Shape {@Overridepublic void draw() {System.out.println("画圆形");}}
}

依赖倒置

高层不应该依赖低层,这样更利于代码结构的升级扩展

先看一段不符合依赖倒置的代码,driver方法参数里面写了具体的车的实现,如果以后要换一种车,只能修改代码实现。

public class CarTest {private static class Benz {public void run() {System.out.println("奔驰跑起来了!");}}private static class Driver {private String name;public Driver(String name) {this.name = name;}// driver方法参数里面写了具体的车的实现,如果以后要换一种车,只能修改代码实现。// 不符合依赖倒置原则public void driver(Benz benz) {benz.run();}}public static void main(String[] args) {Benz benz = new Benz();Driver driver = new Driver("张三");driver.driver(benz);}
}

调整后的代码如下,因为 driver() 方法的参数变成了抽象的 ICar 类型,所以,所有实现 ICar 类型的车都可以调用这个方法,这样就实现了高层不依赖低层。

public class CarTest1 {public static void main(String[] args) {IDriver driver = new Driver();driver.driver(new Benz());driver.driver(new BMW());}private interface ICar {void run();}public static class Benz implements ICar {public void run() {System.out.println("奔驰跑起来了!");}}public static class BMW implements ICar {public void run() {System.out.println("宝马跑起来了!");}}private interface IDriver {void driver(ICar car);}public static class Driver implements IDriver {@Overridepublic void driver(ICar car) {car.run();}}
}

单一职责

一个类只干一件事 便于理解,提高代码的可读性

举个例子,一般来说,系统有登录,注册,注销的功能,可以写成如下形式

public interface UserOperate {void login(UserInfo userInfo);void register(UserInfo userInfo);void logout(UserInfo userInfo);
}public class UserOperateImpl implements UserOperate{@Overridepublic void login(UserInfo userInfo) {// 用户登录}@Overridepublic void register(UserInfo userInfo) {// 用户注册}@Overridepublic void logout(UserInfo userInfo) {// 用户登出}
}

以上设计针对各个方法都不是很复杂的情况,如果每个方法都比较复杂,可以考虑独立成类来做,示例如下

public interface Register {void register();
}public interface Login {void login();
}public interface Logout {void logout();
}public class RegisterImpl implements Register{@Overridepublic void register() {// 用户注册}
}public class LoginImpl implements Login{@Overridepublic void login() {// 用户登录}
}public class LogoutImpl implements Logout{@Overridepublic void logout() {// 登出}
}

这就实现了单一职责的原则。

接口隔离

一个接口只干一件事,功能解耦,高聚合,低耦合

举个例子,学生成绩管理程序一般包含查询成绩、新增成绩、删除成绩、修改成绩、计算总分、计算平均分、打印成绩信息等功能,如果都写在一个类里面,就会变成如下形式

public interface IStudentScore {// 查询成绩public void queryScore();// 修改成绩public void updateScore();// 添加成绩public void saveScore();// 删除成绩public void delete();// 计算总分public double sum();// 计算平均分public double avg();// 打印成绩单public void printScore();
}

这样的方式不利于扩展. 比如:

学生只有查看成绩,打印成绩单的权限, 没有增删改的权限;

老师拥有所有的权限。

采用接口隔离原则设计,可以做如下改进

public interface IQueryScore {// 查询成绩public void queryScore();// 打印成绩单public void printScore();
}public interface IOperateScore {// 修改成绩public void updateScore();// 添加成绩public void saveScore();// 删除成绩public void delete();// 计算总分public double sum();// 计算平均分public double avg();
}

分为查询接口和操作接口,这样学生端就不需要重写和他不相关的接口了。

迪米特法则

不该知道的不要知道,减少代码臃肿

示例:一个人用咖啡机煮咖啡的过程,例子中只有两个类,一个是人,一个是咖啡机。

首先是咖啡机类 CoffeeMachine ,咖啡机制作咖啡只需要三个方法

第一步:加咖啡豆;

第二步:加水;

第三步:制作咖啡。

如果把咖啡机的所有方法暴露给人调用,就会出现如下代码

/*** 咖啡机抽象接口*/
public interface ICoffeeMachine {//加咖啡豆void addCoffeeBean();//加水void addWater();//制作咖啡void makeCoffee();
}/*** 咖啡机实现类*/
public class CoffeeMachine implements ICoffeeMachine{//加咖啡豆public void addCoffeeBean() {System.out.println("放咖啡豆");}//加水public void addWater() {System.out.println("加水");}//制作咖啡public void makeCoffee() {System.out.println("制作咖啡");}
}/*** 人, 制作咖啡*/
public interface IMan {/*** 制作咖啡*/void makeCoffee();
}/*** 人制作咖啡*/
public class Man implements IMan {private ICoffeeMachine coffeeMachine;public Man(ICoffeeMachine coffeeMachine) {this.coffeeMachine = coffeeMachine;}/*** 制作咖啡*/public void makeCoffee() {coffeeMachine.addWater();coffeeMachine.addCoffeeBean();coffeeMachine.makeCoffee();}
}/*** 客户端*/
public class Client {public static void main(String[] args) {ICoffeeMachine coffeeMachine = new CoffeeMachine();IMan man = new Man(coffeeMachine);man.makeCoffee();}
}

其实人根本不关心咖啡机具体制作咖啡的过程。所以我们可以作如下优化:

优化后的咖啡机类,只暴露一个 work() 方法,把制作咖啡的三个具体的方法addCoffeeBean()addWater()makeCoffee()设为私有,work()方法封装了这三个具体方法的实现。

/*** 咖啡机抽象接口*/
public interface ICoffeeMachine {//咖啡机工作void work();}/*** 咖啡机实现类*/
public class CoffeeMachine implements ICoffeeMachine {//加咖啡豆private void addCoffeeBean() {System.out.println("放咖啡豆");}//加水private void addWater() {System.out.println("加水");}//制作咖啡private void makeCoffee() {System.out.println("制作咖啡");}@Overridepublic void work() {addCoffeeBean();addWater();makeCoffee();}
}/*** 人, 制作咖啡*/
public interface IMan {/*** 制作咖啡*/void makeCoffee();
}/*** 人制作咖啡*/
public class Man implements IMan {private ICoffeeMachine coffeeMachine;public Man(ICoffeeMachine coffeeMachine) {this.coffeeMachine = coffeeMachine;}/*** 制作咖啡*/public void makeCoffee() {coffeeMachine.work();}
}/*** 客户端*/
public class Client {public static void main(String[] args) {ICoffeeMachine coffeeMachine = new CoffeeMachine();IMan man = new Man(coffeeMachine);man.makeCoffee();}
}

通过减少 CoffeeMachine 对外暴露的方法,减少 Man 对 CoffeeMachine 的了解,从而降低了它们之间的耦合。

里氏替换原则

子类重写方法功能发生改变,不应该影响父类方法的含义,防止继承泛滥。

比如下述代码,就不符合里氏替换原则

class A{public int func1(int a, int b){return a-b;}
}public class Client{public static void main(String[] args){A a = new A();System.out.println("100-50="+a.func1(100, 50));System.out.println("100-80="+a.func1(100, 80));}
}

接下来需要增加一个新的功能:完成两数相加,然后再与 100 求和,由类 B 来负责。即类 B 需要完成两个功能:

  1. 两数相减。

  2. 两数相加,然后再加100。

由于类 A 已经实现了第一个功能,所以类 B 继承类 A 后,只需要再完成第二个功能就可以了,代码如下:

class B extends A{public int func1(int a, int b){return a+b;}public int func2(int a, int b){return func1(a,b)+100;}
}public class Client{public static void main(String[] args){B b = new B();System.out.println("100-50="+b.func1(100, 50));System.out.println("100-80="+b.func1(100, 80));System.out.println("100+20+100="+b.func2(100, 20));}
}

类B完成后,运行结果:

100-50=150
100-80=180
100+20+100=220

可以发现原本运行正常的相减功能发生了错误。原因就是类 B 在给方法起名时无意中重写了父类的方法,造成所有运行相减功能的代码全部调用了类 B 重写后的方法,造成原本运行正常的功能出现了错误。在本例中,引用基类 A 完成的功能,换成子类 B 之后,发生了异常。在实际编程中,我们常常会通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的几率非常大。如果非要重写父类的方法,比较通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖、聚合,组合等关系代替

UML 和 代码

UML 图

代码

更多

设计模式学习专栏

参考资料

设计模式六大原则(六)----开闭原则

设计模式六大原则(三)----依赖倒置原则

设计模式六大原则(一)----单一职责原则

设计模式六大原则(四)----接口隔离原则

设计模式六大原则(五)----迪米特法则

设计模式六大原则(二)----里式替换原则

设计模式学习(一):设计原则相关推荐

  1. 设计模式01-七大设计原则

    设计模式01-七大设计原则 文章目录 设计模式01-七大设计原则 开闭原则-Open Close 依赖倒置原则-Dependence Inversion 单一职责原则-Simple ResponsiB ...

  2. 设计模式的六大设计原则

    设计模式的六大设计原则 1. 开闭原则 1.1 开闭原则:Open Closed Principle,OCP 1.2 开闭原则的作用 2. 单一责任原则 2.1 单一职责原则:Single respo ...

  3. IOS设计模式的六大设计原则之开放-关闭原则(OCP,Open-Close Principle)

    定义 一个软件实体(如类.模块.函数)应当对扩展开放,对修改关闭. 定义解读 在项目开发的时候,都不能指望需求是确定不变化的,大部分情况下,需求是变化的.那么如何应对需求变化的情况?这就是开放-关闭原 ...

  4. 设计模式之六大设计原则【入门】

    设计模式之六大设计原则 1 开闭原则 Open Closed Principle,OCP 1.1 概念 1.2 软件实体 1.3 开闭原则的作用 2. 单一职责原则 Single responsibi ...

  5. 设计模式 — 6大设计原则(依赖倒置和接口隔离原则)

    设计模式 依赖倒置原则 示例 一 示例 二 依赖的三种写法 总结 接口隔离原则 实例 一 总结 依赖倒置原则 依赖倒置原则(Dependence Inversion Principle,DIP)这个名 ...

  6. 深度学习网络设计原则

    深度学习网络设计原则 输出特征图的大小逐渐减小 在网络较深层应该利用更多的feature map Bottleneck layer的设计 同时增加网络的深度和宽度   翻译及理解自论文Rethinki ...

  7. [转] 设计模式的六大设计原则

    转载说明: 感谢原作者吕震宇老师的分享. 原文参考链接:https://www.cnblogs.com/zhenyulu/category/6930.html? 本次转载只用于个人学习使用,并不涉及商 ...

  8. 代码质量评判标准、设计模式、面向对象设计原则速查表

    文章目录 代码质量评判标准 软件腐化的原因 提高系统可复用性的几点原则 可维护性与可复用性并不完全一致 面向对象设计原则 1. 面向对象设计的六大设计原则表 2. 图解面向对象涉及的六大原则 1. 开 ...

  9. 设计模式---面向对象的设计原则概述

    对于面向对象软件系统的设计而言,在支持可维护性的同时,提高系统的可复用性是一个至关重要的问题,如何同时提高一个软件系统的可维护性和可复用性是面向对象设计需要解决的核心问题之一.在面向对象设计中,可维护 ...

  10. 设计模式-02.经典设计原则-第一节-单一职责原则,开闭原则,里式替换,接口隔离【万字长文系列】

    文章目录 设计模式经典设计原则-第一节 单一职责原则(SRP) 如何理解单一职责原则? 如何判断类的职责是否足够单一? 类的职责是否设计得越单一越好? 开闭原则(OCP) 如何理解"对扩展开 ...

最新文章

  1. 全奖博士 | 美国康涅狄格大学计算机科学与工程系
  2. Curator counters
  3. 特性开关框架 java_关于Mosfet你应当知道的开关特性
  4. 财务用计算机可以一次摊销,购入的电脑可以一次性计提折旧吗
  5. Jupyter中打印所有结果的解决办法
  6. HTML+CSS+JS实现 ❤️响应式图文卡片滑块展示特效❤️
  7. html5 Canvas 绘制基本图形 从直线图形到使用路径 - 直线、矩形、路径、多边形、复杂组合图形
  8. 如何完成知识问答环节中的前期设置,题目准备及现场操作等主要流程?
  9. Windows禁止ping回显
  10. 极智AI | 量化实现分享一:详解 min-max 对称量化算法实现
  11. MySQL几种常见的数据类型
  12. javascript优缺点
  13. 使用python读取mid/mif文件
  14. python transforms_pytorch中的transforms模块实例详解
  15. 看了几个技术入股的帖子,忍不住写个自己亲身经历吧
  16. Espresso Idling Resource
  17. 29.Silverlight碰撞测试、检测自定义控件碰撞,雷达扫描图之扫描雷达点状态
  18. 爬虫工具 AppCrawler
  19. 【转载】CodeWarrior IDE使用Tips-如何编译生成和调用静态库
  20. LOJ10155 一本通1577 数字转换 题解

热门文章

  1. 用JAVA编写50以内的素数_java求50以内的素数
  2. 错误解决——浏览器可以正常上网,但是QQ,微信登不了
  3. 使用蒲公英给iOS应用做内测
  4. 210 裸机程序烧录
  5. Swift-高阶函数如map,reduce,filter的一些总结
  6. IT沙龙 共同成长计划
  7. Day5:计数质数(埃拉托色尼筛选法)
  8. 研究型论文_具有 ExtraTrees 特征选择、极限学习机集成和 softmax 聚合的多层入侵检测系统(英文论文)
  9. mysql外键和自动自增
  10. 脑科学读物阅读笔记系列 - 拉马钱德兰《脑中魅影》- 0.前言