六大设计原则
来源:互联网 发布:查看8080端口被占用 编辑:程序博客网 时间:2024/06/15 16:46
前言
设计原则与设计模式的区别?
设计原则是架构的灵魂,设计模式是具体的实现。
设计原则是一种知道思想,往大了说就像马列主义毛泽东思想一样。设计原则在实际开发中指导着我们完成代码。设计模式就是在完成代码时的具体实现了。
下面我们就具体的来说说这六大原则到底是什么,如何实现这六大原则。
一、单一职责原则
1、定义
单一职责原则(SRP:Single responsibility principle)又称单一功能原则。它规定一个类应该只有一个发生变化的原因。
2、特点
- 降低类的复杂性, 对类或接口的职责有清晰明确定义
- 提高可读性
- 提高可维护性
- 降低变更引起的风险, 接口改变只影响相应的实现类, 不影响其他类
3、实例
了解定义和特点后,我们来通过实例来看看如何设计单一职责原则。
示例要求:
创建一个绘图系统
1、绘图:可以绘制圆形和矩形等
2、显示:显示绘制好的图形
通过示例要求我们知道该示例有两个功能,绘图和显示。
一般的实现结果代码如下
public class DrawPicture{ //绘制圆形 public void drawCircle(){} //绘制矩形 public void drawRect(){} //显示圆形 public void showCircle(){} //显示矩形 public void showRect(){} }
在上述代码中我们可以看到DrawPicture类完成了示例的要求,但是它将绘制和显示都放到了同一个类中去完成了,并没有实现单一职责。示例要求中有两个职责绘制和显示,所以上面代码并不符合单一职责的原则,所以我们该如何来实现呢?
单一职责代码如下
定义绘制功能的接口,如下
public interface IDraw{ //绘制圆形 public void drawCircle(); //绘制矩形 public void drawRect(); }
定义显示功能的接口,如下
public interface IShow{ //显示圆形 public void showCircle(); //显示矩形 public void showRect(); }
然后定义类实现上述两个接口,实现方法
public class Picture implements IDraw,IShow { //绘制圆形 public void drawCircle(){} //绘制矩形 public void drawRect(){} //显示圆形 public void showCircle(){} //显示矩形 public void showRect(){} }
这样就实现了单一职责的原则。
4、重点
- 接口一定要做到单一职责
- 类的单一职责比较难实现,尽量做到只有一个原因引起变化
- 一个方法尽可能做一件事,能分解就分解,分解到原子级别
上面几点就是单一职责需要注意的地方,通过我们上面的示例也可以看出来,虽然在开发中有时候我们很难做到真正的单一职责原则,单要尽量的去符合单一职责原则。
二、里氏替换原则
1、定义
所有引用基类的地方必须能透明地使用其子类的对象;
其实简单理解就是继承。
2、优缺点
里氏替换原则核心是继承,同样它的优缺点也是继承的优缺点
优点
- 代码共享 : 共享代码, 子类都拥有父类的方法和属性, 将 父类的代码共享给了子类;
- 重用性 : 提高代码的重用性, 子类重用父类的代码;
- 子父类异同 : 子类形似父类, 异于父类, 父子都不同;
- 扩展性 : 提高代码的可扩展性, 子类就可以为所欲为了, 子类可以随意扩展父类;
- 开放性 : 提高产品或项目的开放性, 父类随意扩展, 开放性随之增加了;
- 将子类单独作为一个业务来使用, 会让代码间的耦合关系都复杂, 缺乏类替换标准
缺点
- 侵入性 : 继承是侵入性的, 子类 强制继承 父类的方法和属性;
- 灵活性 : 降低代码的灵活性, 子类必须拥有父类的属性和方法, 子类收到了父类的约束, 这是从子类的角度讲得;
- 耦合性 : 增强了耦合性, 父类的属性和方法被修改时, 还需要顾及其子类, 可能会带来大量的重构, 这是从父类的角度讲的;
- 将子类当做父类用, 抹杀了子类的个性;
3、重点
- 返回值: 父类方法返回值类型 F, 子类方法返回值类型 S, 里氏替换原则是 S 范围必须小于 F;
- 重载:父类 子类 方法参数类型或者数量不同, 如果要符合里氏替换要求的话, 子类参数必须 >= 父类参数, 即不能让子类自己定义的方法被调用;
4、注意点
如果想要使用里氏替换, 尽量避免让子类拥有自己单独的成员变量 或者 方法, 如果子类个性多了, 子类父类关系很难调和
三、依赖倒置原则
1、定义
高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
抽象不依赖细节 : 抽象不依赖细节, 接口或抽象类不依赖与实现类;
2、问题由来
类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。
出现了上面的问题,我们该如何解决呢?
解决方案
将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。
3、实例
2中出现的问题也给出了解决方案,但是我们该如何在代码中完成呢?下面就来看看如何在代码中实现吧。
首先这里有个需求
场景 母亲给孩子讲故事,只要给她一本书,她就可以照着书给孩子讲故事了
普通的实现我们可以这么完成,代码如下
定义Book类
class Book { public String getContent(){ //书中故事的内容 return "白雪公主与小矮人在山里......."; } }
定义Mother类
class Mother{ //妈妈讲故事 public void read(Book book){ System.out.println("妈妈:开始讲故事了"); System.out.println("故事: "+book.getContent()); } }
调用,完成妈妈讲故事
public static void main(String[] args) { //一本书 Book book=new Book(); //一个妈妈 Mother mother=new Mother(); //妈妈按照书中的内容讲故事 mother.read(book); }
普通的就是上述方式完成,虽然可以实现妈妈讲故事了,但是突然有一天,妈妈不想讲书中的故事了,想读报纸,那么该怎么办呢,难道我们需要将妈妈内部的方法内容改变吗,当然在上面的代码中我们需要改变,所以这就不符合依赖倒置原则了。
在这个例子中Mother类是高层模块,Book是底层模块,在依赖倒置原则中高层模块不需要依赖底层模块,但是上面代码中Mother依赖了Book,所以我们需要对代码进行修改。
首选我们抽象一个接口出来,作为所有读物的父接口
interface IReadContent { public String getContent(); }
让书和报纸实现这个接口
class Book implements IReadContent{ public String getContent(){ return "白雪公主与小矮人在山里......."; } } class NewsPager implements IReadContent { public String getContent(){ return "报纸上说 白雪公主与小矮人在山里......."; } }
高层Mother模块
class Mother{ public void read(IReadContent book) { System.out.println("妈妈:开始讲故事了"); System.out.println("故事: "+book.getContent()); } }
通过这样修改后,高层Mother模块依赖的是接口IReadContent,底层模块Book和NewsPager各自实现了接口IReadContent,这样就大大降低了修改类Mother的风险了。
4、好处
- 减少类之间的耦合
- 提高系统的稳定性
- 降低并发风险
- 提高代码的可读性
通过上述代码实例我们也能看出这几点好处了。
5、实现
- 1、构造函数依赖对象:注入方法 : 通过 构造函数参数 声明依赖对象, 即构造函数注入;
- 2、setter方法依赖对象:通过 Setter 函数 参数 声明依赖对象, 即构造函数注入;
- 3、接口注入依赖对象:在接口方法的参数中声明依赖对象, 即接口注入;
上面的例子中我采用的就是接口注入依赖对象思想。
6、本质
通过 抽象 即 接口或者抽象类, 使 各个类 和 模块实现彼此独立, 实现模块间 松耦合;
7、注意点
尽量不要覆盖方法, 如果方法在抽象类中已经实现, 子类不要覆盖;
8、使用场景
- 依赖倒置在小项目中得有点很难体现出来, 是否采用依赖倒置原则影响不大;
- 项目越大, 需求改变越多, 采用依赖倒置原则设计的接口或抽象类 对 实现类的约束, 会大大减少维护成本
四、接口隔离原则
1、定义
接口隔离定义 : 建立单一的接口, 功能尽量细化, 不要建立臃肿的接口;
- 不需要的接口 : 客户端尽量不依赖其不需要的接口, 客户端需要什么接口就提供什么接口, 剔除不需要的接口, 对接口进行细化, 保持接口方法最少;
- 最小接口 : 类间的依赖关系应该建立在最小接口上, 细化接口;
2、单一职责与接口隔离的区别
- 单一职责:注重职责,注重业务逻辑上的划分
- 接口隔离:注重的是接口的方法尽量少
所以两个的区别其实是针对的方向或对象不同
3、特点
接口尽量小
- 拆分接口 : 接口隔离的核心定义, 不出现臃肿的接口;
- 限制 : 接口小有限度, 不能违反单一职责原则, 不要将一个业务逻辑拆分成两个接口;
- 要求 : 根据接口隔离原则拆分接口时, 必须满足单一职责原则;
- 拆分接口 : 接口隔离的核心定义, 不出现臃肿的接口;
接口高内聚
- 高内聚 : 提高接口, 类, 模块的处理能力, 减少对外界交互;
- 具体方法 : 接口中尽量少公布public 方法, 对外公布的 public 方法越少, 变更的风险就越小, 有利于后期的维护;
定制服务
- 起源 : 系统模块间的耦合需要有相互访问的接口, 这里就需要为各个 客户端 的访问提供定制的服务接口;
- 要求 : 只提供访问者需要的方法, 不需要的就不提供;
接口隔离限度
- 粒度小 : 接口粒度越小, 系统越灵活, 但是同时使系统结构复杂, 开发难度增加, 降低了系统的可维护性;
- 粒度大 : 灵活性降低, 无法提供定制服务, 增大项目风险;
4、原子接口划分原则
- 接口模块对应关系 : 一个接口只服务于一个子模块 或 业务逻辑;
- 方法压缩 : 通过业务逻辑, 压缩接口中得 public 方法, 减少接口的方法的数量;
- 修改适配 : 尽量去修改已经污染的接口, 如果变更风险较大, 采用适配器模式进行转化处理;
5、实例讲解
上面说了这么多,那我们在实际开发中该如何做呢?
比如按钮的点击事件,点击事件有点击和长按两种操作,一般采用接口回调的方式完成。下面看看接口是如何完成。
public interface OnClick{ //点击 public void onClick(); //长按事件 public void onLongClick(); }
上面的代码中我们将点击和长按事件都放在了一个接口中,这样虽然可以实现功能,但是违反了接口隔离原则,我们应该将其更具体的细化
//点击接口 public interface OnClick{ public void onClick(); } //长按事件接口 public interface OnLongClick{ public void onLongClick(); }
这样将这两个功能细化的具体的接口中就可以了。
五、迪米特法则
1、定义
最少知识原则, 一个对象应该对其它对象有最少的了解, 即一个类对自己需要耦合或者调用的类知道的最少;
2、生活中的示例看代码
朋友间必须保持距离
- 距离太近示例 : 朋友间不能无话不说, 无所不知, 类 A 与 B 耦合, B 将很多方法暴露给 A, 两个类之间的的耦合关系非常牢固, 这明显违反设计原则;
- 保持距离方法 : 将 类 B 暴露给 A 的方法封装, 暴露的方法越少越好, 类 B 高内聚, 与 A 低耦合;
- 设计方法 : 一个类的 public 方法越多, 修改时涉及的范围也就越大, 变更引起的风险也就越大, 在系统设计时要注意, 能用 private 就用private , 能用 protected 就用 protected, 能少用 public 就少用 public, 能加上 final 就加上 final;
3、优缺点
- 优点
- 类间解耦, 弱耦合, 耦合降低, 复用率提高;
- 缺点
- 类间的耦合性太低, 会产生大量的中转或跳转类, 会导致系统的复杂性提高, 加大维护难度;
六、开闭原则
1、定义
软件的实体 类, 模块, 函数 应该对扩展开放, 对修改关闭; 即 软件实体 应该 通过扩展实现变化, 不是通过 修改已有的代码实现变化;
2、优点
- 利于测试 : 如果改变软件内容, 需要将所有的测试流程都执行一遍, 如 单元测试, 功能测试, 集成测试等, 如果只是扩展, 只单独测试扩展部分即可;
- 提高复用性 : 所有逻辑都从原子逻辑组合, 原子逻辑粒度越小, 复用性越大; 这样避免相同逻辑存在, 修改时需要修改多个此相同逻辑;
- 提高可维护性 : 维护一个类最好的方式是 扩展一个类, 而不是修改一个类, 如果需要修改需要读懂源码才能修改, 扩展的话只需要了解即可, 直接继承扩展;
QQ交流群
微信公众号:Android在路上,欢迎关注
- 六大设计原则--开闭原则
- 六大设计原则,开闭原则
- 六大设计原则
- 六大设计原则[笔记]
- 六大OO设计原则
- 六大设计原则
- 设计模式六大原则
- 设计模式六大原则
- 设计模式六大原则
- 设计模式六大原则
- 设计模式六大原则
- 设计模式六大原则
- 六大设计原则
- 设计模式六大原则
- 设计模式六大原则
- 设计模式六大原则
- 设计模式六大原则
- 设计模式六大原则
- HDU 6194 string string string
- JavaScript验证上传文件大小和类型
- java 不常用关键字
- 将keil中的数据绘成波形
- java 开发微信红包
- 六大设计原则
- nginx学习随笔--tcp-nopush
- Java菜鸟------在接受键盘输入只接受整形
- Java递归详解_动力节点Java学院
- 知识付费的时代,你更需要的是思想
- AndroidStudio-使用git&github
- java垃圾收集与内存分配策略
- UGUI 学习笔记 9 Scrollbar 和ScrollView
- BOM浏览器对象模型