设计原则与设计模式
1. 目录
2. 简介
本文是《深入浅出设计模式》的读书笔记、
下文说到的接口分为广义接口和狭义接口。
广义接口包括接口和抽象类。而狭义接口则单指编程语言中的接口(interface)。
2.1. 设计原则
2.1.1. 变化与不变化
找出应用中可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码混合在一起。
把会变化的部分取出并封装起来,一边以后可以轻易地改动或扩充此部分,这样就不会影响到不需要改动的部分了。
2.1.2. 针对接口编程而不是针对实现编程
这里的接口是广义的接口。
面向接口编程是指设计类时,把实现的细节放在接口中,把一些实质动作,会改变的动作抽象成接口,然后在接口中实现,因为这些实现细节是很容易改变的有,因此我们要把他组件化,使其可复用。比如实现“小明在吃苹果”可以针对接口实现“{人接口}在{动作接口}{对象接口}”,然后在三个接口中分别实现小明类,吃动作类,苹果类。以后若还有类似需求,比如小明在切苹果。只要实现切动作类即可。具体的例子看下面。
比如《深入浅出设计模式》中第16页的例子。
要实现鸭子类的“叫”动作和“飞”动作。
如果是面向实现编程则简单的硬编码即可。
而如果面向接口编程则是,定义“叫动作”接口和“飞动作”接口,根据鸭子种类不同(绿头鸭嘎嘎叫,会飞,木头鸭不会叫不会飞,充气鸭吱吱叫不会飞)来组合,使代码可复用,不用一次次定义很多鸭子类。如下:
public class Duck{
QuackBehavior qb; //叫动作接口成员
FlyBehavior fb; //飞动作接口成员
public void quack(){ //叫动作函数
qb.quack();
}
public void fly(){ //飞动作函数
fb.fly();
}
}
//实现叫动作接口示例
//木头鸭不会叫
public class SlientQuack implement QuackBehavior{
public void quack(){
system.out.printout("I can not quack");
}
}
//创建木头鸭子类
public class WoolDuck extend Dcuk{
public WoolDuck(){
this.qb= new SlientQuack();
this.fb = new CanNotFly();
....
}
}
这样一来,在增加新鸭子类的时候,可以通过硬编码方式静态添加,也可以通过构造函数,传入接口动作参数动态生成新的鸭子类。
这样就把实现的细节委托给了接口对象。
2.1.3. 为了交互对象之间的松耦合设计而努力
这能简历有弹性的OO系统,能够应付变化,因为对象之间的相互依赖降到了最低。
2.1.4. 开放-关闭模式
对修改关闭,对扩展开放。
一个优秀的架构应该具有良好的扩展性,而对修改关闭,以避免修改后引入新bug和损坏依赖。
使用装饰器可以满足这个设计原则。扩展时先考虑组合,在考虑继承。
2.1.5. 依赖倒置原则
要依赖抽象而不是依赖具体类。
这个原则很像“面向接口编程”原则,在编程中,我们不能让高层组件直接依赖底层组件,不管高层底层,都应该依赖抽象。这个原则工厂模式总得到了体现。
依赖倒置中的倒置意思见下图:
将高层组件所依赖的组件抽象成抽象类或接口,而底层组件的调用也返回一个抽象类,或者实现这个抽象类。这样就能实现依赖倒置,并且解耦。
看下工厂模式的部分类图
工厂方法和具体产品都依赖产品这个抽象类或接口,也就是解耦合,我们可以不修改代码,就能添加新的子产品类,只要这个类实现了产品类接口。
2.1.6. 单一职责
一个类应该只有一个引起变化的原因。
3. 观察者模式
在Subject中有一个成员变量是Observer类型的数组,里面包含着所有“观察”着Subject的观察者。所以两个接口之间是1对N的聚合关系。
而ConcreteSub和ConcreteObserver是一个动态关联的关系,因为ConcreteObserver的update方法中有Subject类的参数,以此来分辨是哪个ConcreteSubject发来的更新操作。(一个观察者可以观察多个对象)。
3.1. 通知的模式
通知的模式分为push和pull,通常来说push模式更广泛一点,pull会因为多个观察者频发送request而造成网络拥堵。
但pull也有特点,可以让观察者自定义因素询问操作,比如在subject内声明一个changed变量,subject变化不大时,其值为false,只有当observer对象pull时,且change为真,subject才会发出回应,从而使得observer真正执行update操作。
4. 装饰者模式
有时,“继承”这个OOP特性会被滥用,用于构建出一个个和而不同的类,而装饰者模式则可以有效地解决“继承滥用”的问题。装饰者模式使用了“组合”而不是“继承”。
它动态地给一个对象添加职责,以区分和其他类的不同。
通过装饰者模式,还可以解决开放-关闭原则问题,因为装饰者模式并没有修改源代码,而是扩展原来的类。
在Decorator类对象中,会有一个成员变量指向被装饰的对象。这与适配器模式和代理模式相似。
与适配器模式相比较,适配器模式是更改接口,以对接另一个系统或对象。而装饰器模式不改变接口且添加新职责。
与代理模式相比较,装饰器模式更强调“装饰”或“增强”原来的类,而代理模式强调对原来的类做“权限管理”或“访问控制”等,而且做这些“新功能”的主体是代理类。本质来说就是意图不一样。
5. 适配器模式
适配器将一个接口转成另一个接口,使接口不兼容的那些类可以一起工作。
Adaptee本身并不满足Target接口的要求,所以要借助Adapter(适配器)。像Decoratot一样,在Adapter内也维护这一个成员变量指向原来的对象。
调用Adapter的request其实就是调用Adaptee的specilRequest()。
适配器又分为对象适配器和类适配器。
如果要使用类适配器的话,则要求编程语言支持多继承,或者Target是一个狭义接口Adaptee是个类才行。
而对象适配器则只需要实现Target这个广义接口既可以。
6. 代理模式
proxy对象持有对realSubject的引用,所以在必要的时候可以请求发给realSubject。比如,在使用虚拟代理时,对象已经被创建/加载出来了,这个时候就没必要用虚拟代理回应了。使用保护代理时,访问者有足够的权限,就没必要拒绝访问了。
代理模式分为以下三个用途:
- 远程代理,控制访问对象。
- 虚拟代理,控制访问创建开销大的资源,lazy load。
- 保护代理,基于权限控制对资源的访问。
虚拟代理就比如加载网站时,对一些大资源图片等,加载未完成时,浏览器会使用虚拟代理类先给页面返回“加载中,请稍后”等类似图片,一旦大资源被加载了就立即替换。
代理模式和装饰模式都有“修饰”原有对象的作用。两者的区别可以在看下装饰器小节中的总结。总之就是代理模式和装饰模式的意图不一样,代理是用代理类来委托工作,而装饰是为了增强原先的类。
原文地址:https://www.cnblogs.com/Jun10ng/p/12975196.html
- 逆向工厂(二):静态分析技术
- 打开文件夹就运行?COM劫持利用新姿势
- Java集合总览
- 常见面试题之ListView的复用及如何优化
- 自定义圆形控件RoundImageView并认识一下attr.xml
- 自定义带图片和文字的ImageTextButton
- 超值干货:个人开发者如何使用免费又简单的开发后台
- 【周末分享】解决中文排版错位的JustifiedTextview控件
- 超级网络
- c++ fstream + string 处理大数据
- 超炫的FlowingDrawer效果
- 源码分享:仿余额宝数字跳动效果 TextCounter
- 一键清理应用数据或者清除应用缓存的方法
- 开发者必知:谷歌做了一个艰难的决定
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 提高数据分析工作效率-Sublime如何设置默认打开文件格式
- 使用 freemarker 制作代码生成器
- 假期闲的慌,不如做一点SQL基础练习吧
- Vue 中全局过滤器的使用
- 游戏-CSP201712-2-Java
- Mybatis generator 生成 Mapper 方法不全
- Vue 中使用 JQuery 插件不起作用
- 公共钥匙盒-CSP数组排序练习
- 疫情这么严重,还不待家里学Numpy和Pandas?
- CSP-201812-2-小明放学-Java
- Mac怎么设置docker国内镜像源来加速下载?
- 适合数据分析面试笔试入门的编程题
- 女同事问狗哥什么是线程池的阻塞队列?
- BAT某厂数据分析终面面经
- 通过常见的业务掌握SQL高级功能