设计模式(2)六大原则(七大原则)
前言
前面已经明白了UML是什么,UML该在哪里用
接下来开始真正接触设计模式的思想:六大原则
目录
- 六大原则(七大原则)
- 开闭原则
2.1. 开闭原则的作用
2.2. 开闭原则的案例 - 里氏替换原则
3.1. 里氏替换原则的作用
3.2. 里氏替换原则的案例 - 依赖倒置原则
4.1. 依赖倒置的作用
4.2. 依赖倒置的案例 - 单一职责原则
5.1. 单一职责原则优点
5.2. 单一职责原则案例 - 接口隔离原则
6.1. 接口隔离原则的优点
6.2. 接口隔离原则的案例 - 迪米特法则
7.1. 迪米特法则的优点
7.2. 迪米特法则案例 - 合成复用原则
8.1. 合成复用原则的优点
8.2. 合成复用原则案例 - 总结
六大原则
实际是七大原则,几种无所谓,都是为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,都是面向对象原则
分别是:开闭原则、里氏替换原则、依赖倒置原则、单一职责原则、接口隔离原则、迪米特法则、合成复用原则
开闭原则
开闭原则(Open Closed Principle,OCP):软件实体应当对扩展开放,对修改关闭
软件实体包括 :项目中划分出的模块,类与接口, 方法
意思是:当应用软件需求改变时,在不修改软件实体的源代码或者二进制代码的前提下,扩展模块功能,使其实现新的需求(Java中的抽象、封装)
开闭原则的作用
开闭原则是面对对象的可复用设计的第一块基石,是最重要的面向对象设计原则,使软件实体拥有一定的适应性和灵活性的同时具备稳定性和延续性
作用:
- 对软件测试的影响
软件遵守开闭原则的话,软件测试时只需要对扩展的代码进行测试就可以了,因为原有的测试代码仍然能够正常运行。 - 可以提高代码的可复用性
粒度越小,被复用的可能性就越大;在面向对象的程序设计中,根据原子和抽象编程可以提高代码的可复用性。 - 可以提高软件的可维护性
遵守开闭原则的软件,其稳定性高和延续性强,从而易于扩展和维护。
在软件不断升级维护过程中,如果改动软件实体的源代码,可能会引入错误造成需要重构软件,开闭原则就是指导我们不要动用源代码,通过扩展来完成新的需求
开闭原则的案例
Java中可以通过抽象、封装来实现开闭原则
例:一个简单的Car工厂
package com.company.DesignPatterns;abstract class CarFactory {String carType;abstract void produce();
}class BMW extends CarFactory{@Overridevoid produce() {System.out.println("生产宝马中:"+carType+"生产成功");}
}class Productor{public static void main(String [] args){BMW bmw=new BMW();bmw.carType="宝马";bmw.produce();}
}
当我们有新需求:生产AE86,就不需要动用软件实体的源代码,只需扩展一个类
class AE extends CarFactory{@Overridevoid produce() {System.out.println("生产AE86:"+carType+"生产成功");}
}
开闭原则是面向对象的最重要的原则,提升了代码的可复用性和可维护性
里氏替换原则
里氏替换原则(Liskov Substitution Principle,LSP)主要阐述了有关继承的一些原则,也就是什么时候应该使用继承,什么时候不应该使用继承,以及其中蕴含的原理。
里氏替换原是继承复用的基础,它反映了基类与子类之间的关系,是对开闭原则的补充,是对实现抽象化的具体步骤的规范
里氏替换原则的作用
- 里氏替换原则是实现开闭原则的重要方式之一。
- 它克服了继承中重写父类造成的可复用性变差的缺点。
- 它是动作正确性的保证。即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性。
里氏替换原则的案例
里氏替换原则明显就是定义了继承的原则:子类可以扩展父类的功能,但不能改变父类原有的功能,就是不能重写功能
上面的例子父类是抽象类还好,不会碰到这个问题,如果是普通父类
有一个著名的案例:几维鸟不是鸟
public class LSPtest
{public static void main(String[] args){Bird bird1=new Swallow();Bird bird2=new BrownKiwi();bird1.setSpeed(120);bird2.setSpeed(120);System.out.println("如果飞行300公里:");try{System.out.println("燕子将飞行"+bird1.getFlyTime(300)+"小时.");System.out.println("几维鸟将飞行"+bird2.getFlyTime(300)+"小时。");}catch(Exception err){System.out.println("发生错误了!");}}
}
//鸟类
class Bird
{double flySpeed;public void setSpeed(double speed){flySpeed=speed;}public double getFlyTime(double distance){return(distance/flySpeed);}
}
//燕子类
class Swallow extends Bird{}
//几维鸟类
class BrownKiwi extends Bird
{public void setSpeed(double speed){flySpeed=0;}
}
几维鸟子类对父类方法进行了重写,给原系统引进了错误
依赖倒置原则
依赖倒置原则(Dependence Inversion Principle,DIP)的原始定义为:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象
核心思想是:要面向接口编程,不要面向实现编程
就是使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给它们的实现类去完成
这让我想起来Spring的核心思想:依赖注入、控制反转
依赖倒置的作用
- 依赖倒置原则可以降低类间的耦合性。
- 依赖倒置原则可以提高系统的稳定性。
- 依赖倒置原则可以减少并行开发引起的风险。
- 依赖倒置原则可以提高代码的可读性和可维护性
依赖倒置的案例
高耦合:
class Customer
{public void shopping(Shop shop){//购物System.out.println(shop.sell());}
}
对于一个购物类,每次购物都需要不同的Shop实例,这违反了开闭原则
依赖倒置:
package com.company.DesignPatterns;class Customer
{public void shopping(ShopType shopType){shopType.toShop();}
}
interface ShopType{public void toShop();
}class FoodShop implements ShopType{@Overridepublic void toShop() {System.out.println("前往食品店");}
}
class ClothesShop implements ShopType{@Overridepublic void toShop() {System.out.println("前往服装店");}
}class Test{public static void main(String[] args){Customer customer=new Customer();customer.shopping(new FoodShop());customer.shopping(new ClothesShop());}
}
依赖倒置之后,我们再也不需要改变Customer类的方法,只需在需要使用时注入ShopType类就完成
单一职责原则
单一职责原则(Single Responsibility Principle,SRP)规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分
就是类只能承担一个职责,多个职责会造成:
一个职责的变化可能会削弱或者抑制这个类实现其他职责的能力
当客户端需要该对象的某一个职责时,不得不将其他不需要的职责全都包含进来,从而造成冗余代码或代码的浪费
单一职责原则优点
单一职责原则的核心就是控制类的粒度大小、将对象解耦、提高其内聚性。如果遵循单一职责原则将有以下优点。
- 降低类的复杂度。一个类只负责一项职责,其逻辑肯定要比负责多项职责简单得多
- 提高类的可读性。复杂性降低,自然其可读性会提高
- 提高系统的可维护性。可读性提高,那自然更容易维护了
- 变更引起的风险降低。变更是必然的,如果单一职责原则遵守得好,当修改一个功能时,可以显著降低对其他功能的影响
单一职责原则案例
对学生的工作有两个职责:生活辅导、学业指导,这两个职责分给辅导员和学业导师去完成,即单一职责原则(这里的职责看具体分类)
接口隔离原则
接口隔离原则(Interface Segregation Principle,ISP)要求程序员尽量将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含客户感兴趣的方法
即客户端不应该被迫依赖与它不使用的方法,一个类对另一个类的依赖应该建立在最小的接口
接口隔离与单一职责有一定的相似:都是为了提高类的内聚性、降低它们之间的耦合性,体现了封装的思想
两者的区别:
单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离
单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建
接口隔离原则的优点
接口隔离原则是为了约束接口、降低类对接口的依赖性,遵循接口隔离原则有以下 5 个优点。
将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性
接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的耦合性
如果接口的粒度大小定义合理,能够保证系统的稳定性;但是,如果定义过小,则会造成接口数量过多,使设计复杂化;如果定义太大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险
使用多个专门的接口还能够体现对象的层次,因为可以通过接口的继承,实现对总接口的定义
能减少项目工程中的代码冗余。过大的大接口里面通常放置许多不用的方法,当实现这个接口的时候,被迫设计冗余的代码
接口隔离原则的案例
接口隔离就是提供角色需要的方法,屏蔽不需要的方法
一个接口代表一个角色,不应当将不同的角色都交给一个接口
例:吃早餐分为吃、喝
接口隔离原则就是吃早餐要分成吃接口和喝接口
吃接口只负责吃各种东西
喝接口只负责喝各种东西
package com.company.DesignPatterns;interface Drink{public void drink();
}
interface Eat{public void eat();
}
class DrinkM implements Drink{@Overridepublic void drink() {System.out.println("正在喝牛奶!");}
}
class DrinkW implements Drink{@Overridepublic void drink() {System.out.println("正在喝水!");}
}class EatB implements Eat{@Overridepublic void eat() {System.out.println("正在吃面包!");}
}
class EatN implements Eat{@Overridepublic void eat() {System.out.println("正在吃面条!");}
}class Breakfast{public static void main(String[] args){EatB eatB=new EatB();eatB.eat();EatN eatN=new EatN();eatN.eat();DrinkM drinkM=new DrinkM();drinkM.drink();DrinkW drinkW=new DrinkW();drinkW.drink();}}
迪米特法则
迪米特法则(Law of Demeter,LoD)又叫作最少知识原则(Least Knowledge Principle,LKP)
迪米特法则的定义是:只与你的直接朋友交谈,不跟“陌生人”说话
含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性
迪米特法则的优点
迪米特法则要求限制软件实体之间通信的宽度和深度,正确使用迪米特法则将有以下两个优点。
- 降低了类之间的耦合度,提高了模块的相对独立性。
- 由于亲合度降低,从而提高了类的可复用率和系统的扩展性
迪米特法则案例
明星与经纪人:
明星将与粉丝见面、与媒体公司洽谈业务交给经纪人
即明星仅与经纪人交谈
package com.company.DesignPatterns;public class LoDtest
{public static void main(String[] args){Agent agent=new Agent();agent.setStar(new Star("Jay"));agent.setFans(new Fans("粉丝LJJ"));agent.setCompany(new Company("音乐有限公司"));agent.meeting();agent.business();}
}
//经纪人
class Agent
{private Star myStar;private Fans myFans;private Company myCompany;public void setStar(Star myStar){this.myStar=myStar;}public void setFans(Fans myFans){this.myFans=myFans;}public void setCompany(Company myCompany){this.myCompany=myCompany;}public void meeting(){System.out.println(myFans.getName()+"与明星"+myStar.getName()+"见面了。");}public void business(){System.out.println(myCompany.getName()+"与明星"+myStar.getName()+"洽淡业务。");}
}
//明星
class Star
{private String name;Star(String name){this.name=name;}public String getName(){return name;}
}
//粉丝
class Fans
{private String name;Fans(String name){this.name=name;}public String getName(){return name;}
}
//媒体公司
class Company
{private String name;Company(String name){this.name=name;}public String getName(){return name;}
}
合成复用原则
合成复用原则(Composite Reuse Principle,CRP)又叫组合/聚合复用原则(Composition/Aggregate Reuse Principle,CARP)
合成复用原则:要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现
意思就是:尽量通过引用对象(UML中的组合、聚合)来实现复用
合成复用原则的优点
类的复用分为:继承复用、合成复用
继承复用虽然有简单和易实现的优点,但它也存在以下缺点。
- 继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为“白箱”复用
- 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护
- 它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化
合成复用的优点:
- 它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用
- 新旧类之间的耦合度低。这种复用所需的依赖较少,新对象存取成分对象的唯一方法是通过成分对象的接口
- 复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象
合成复用原则案例
继承复用:
汽油汽车、电动汽车继承汽车,然后汽油汽车、电动汽车又有很多子类
明显破坏了父类的封装性,且父类子类耦合度过高,每次改动父类都会所有子类
合成复用:
将颜色、驱动各作为一个引用,这样想要创建一个新的颜色、驱动类型都很简单
总结
设计模式的七大原则:
- 开闭原则是核心,指导对扩展开放,对修改关闭
- 里氏替换原则:子类不能重写父类
- 依赖倒置原则:面向接口编程,通过接口规定编程规范,然后使用类实现
- 单一职责原则:一个类应当只负责一个职责
- 接口隔离原则:只给角色提供需要的方法,设计接口要精简单一
- 迪米特法则:降低耦合,不需直接调用的类通过第三方转发调用
- 合成复合法则:尽量先考虑合成复合,少用继承复合
- 开闭原则是核心,其他六个原则各侧重点不同,这七个原则即是面对对象设计的思想
设计模式(2)六大原则(七大原则)相关推荐
- 【设计模式】软件设计七大原则
做好自己 软件设计七大原则 开闭原则OCP(Open-Closed Principle) 依赖倒转原则DIP(Dependence Inversion Principle) 单一职责原则SRP(Sin ...
- Java设计模式总结篇:七大原则以及23种设计模式简单介绍
java设计模式 一.前言 二.七大原则 1.单一原则 2.开闭原则 3.里氏替换原则 4.接口隔离原则 5.依赖倒置原则 6.迪米特法则 7.合成复用原则 三.23种设计模式 1.单例模式(Sing ...
- 设计模式(面向对象)设计的七大原则
声明:本人设计模式模块是集合网上资料和老师课件总结的知识点,如本博客有侵权,本人即刻删. 设计模式(面向对象设计)原则,分别是: 1.开放封闭原则:对扩展开放,对修改关闭 2.单一职责原则:一个类只做 ...
- 实例图解设计模式之面向对象设计七大原则
文章目录 面向对象设计原则详解 面向对象设计原则概述 常用的面向对象设计原则 单一职责原则(Single Responsibility Principe)SRP ★★★★☆ 开闭原则(Open-Clo ...
- 设计模式 - 软件设计的七大原则
文章目录 前言 1. 软件设计模式原则 2. 开闭原则 1. 概念 2. 例子 3. 里氏替换原则 1. 概念 2. 例子 4. 依赖倒置原则 1. 概念 2. 代码 5. 单一职责原则 说明 6. ...
- 【设计模式】软件设计七大原则 ( 合成复用原则 | 代码示例 )
文章目录 一.合成复用原则简介 二.合成复用 与 继承复用 优缺点 三.合成复用原则代码示例 1.继承复用代码示例 2.合成复用代码示例 一.合成复用原则简介 合成复用原则 又称为 组合复用原则 , ...
- 【设计模式】软件设计七大原则 ( 里氏替换原则 | 定义 | 定义扩展 | 引申 | 意义 | 优点 )
文章目录 一.里氏替换原则定义 二.里氏替换原则定义扩展 三.里氏替换原则引申意义 四.里氏替换原则意义 五.里氏替换原则优点 一.里氏替换原则定义 里氏替换原则定义 : 如果 对每一个 类型为 T1 ...
- 【设计模式】软件设计七大原则 ( 接口隔离原则 | 代码示例 )
文章目录 一.接口隔离原则简介 二.接口隔离原则代码示例 ( 反面示例 ) 1.接口定义 ( 接口臃肿 ) 2.实现类 1 3.实现类 2 三.接口隔离原则代码示例 ( 推荐用法 ) 1.接口 1 2 ...
- 【设计模式】软件设计七大原则 ( 依赖倒置原则 | 代码示例 )
文章目录 一.依赖倒置原则简介 二.面向实现编程代码示例 ( 反面示例 ) 1.顾客类 2.测试类 三.依赖倒置原则代码示例 ( 推荐示例 | 使用接口方法注入 IGood 实现类 ) 1.顾客类 2 ...
- 【设计模式】软件设计七大原则 ( 开闭原则 )
文章目录 一.开闭原则简介 二.开闭原则代码示例 1.商品接口 2.普通商品类 3.折扣商品类 4.测试类 一.开闭原则简介 开闭原则 : 定义 : 一个 软件实体 , 类 / 模块 / 函数 , 对 ...
最新文章
- 100题_10 在排序数组中查找和为给定值的两个数字
- 输入一个正整数,求它各位数的数字之和
- 一起学nRF51xx 10 - rng
- 连接mongodb,kafka异步处理代码
- 通过cookie保存并读取用户登录信息
- uvalive4836(枚举)
- python面向对象:多态与多态性
- 前端图片上坐标连线_前端图形学(十三)——弹跳运动的深入之傲娇的小球
- 从零开始刷Leetcode——数组(118.119.121)
- 用友nc操作手册_铁军人物汤轩宇, 入职两年,她用努力和汗水编制出单户试算操作手册...
- 100套PPT模板用于论文答辩、工作方案等
- 高仿城通网盘php,城通网盘VIP订单存在业务隐患,可在线提权(含详细修复)
- python爬取谷歌学术_对于python抓取google搜索结果的一些了解
- 激活office 2007 的方法
- 穿越派·派盘 + 思源笔记 = 私人笔记本
- 这样美化PPT图表,真的好看到爆~
- BDD 100K数据集label转换为yolo训练格式
- ESP32温湿度 DHT11 实验
- [原创]智能化运维平台部署(gunicorn+nginx+gevent+supervisor部署flask+vue)
- 闪游浏览器 0.8 发布,超强地址栏增强等10多项改进