编写软件过程中,程序员面临着来自 耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性 等多方面的挑战, 设计模式是为了让程序(软件),具有如下更好的特性
- 代码重用性 (即:相同功能的代码,不用多次编写)
- 可读性 (即:编程规范性,便于其他程序员的阅读和理解)
- 可扩展性 (即:当需要增加新的功能时,非常的方便,称为可维护)
- 可靠性 (即:当我们增加新的功能后,对原来的功能没有影响)
使程序呈现高内聚, 低耦合的特性
那么一个设计模式在这样设计的时候,要遵循哪些原则呢,即设计模式为什么这么设计的依据, 下面开始介绍,设计模式的 七大原则
- 单一职责原则
- 接口隔离原则
- 依赖倒转(倒置)原则
- 里氏替换原则
- 开闭原则
- 迪米特法则
- 合成复用原则
开闭原则
对扩展开放,对修改关闭,不能去修改原有的代码,实现一个热插拔的效果,易于维护和升级,
想要达到这种效果,我们需要使用接口和抽象类
创建抽象类
public abstract class AbstratSkin { public abstract void display(); }
public class DefaultSkin extends AbstratSkin { @Override public void display() { System.out.println("默认颜色"); } }
public class HeiSkin extends AbstratSkin{ @Override public void display() { System.out.println("黑色"); } }
可以理解为具体业务,去执行程序的
public class Input { private AbstratSkin skin; public void setSkin(AbstratSkin skin) { this.skin = skin; } public void display() { skin.display(); } }
public class Client { public static void main(String[] args) { Input input = new Input(); DefaultSkin defaultSkin = new DefaultSkin(); input.setSkin(defaultSkin); input.display(); } }
里式替换原则:
是面向对象设计的基本原则之一.
任何基类可以出现的地方,子类一定可以出现,通俗理解:子类可以拓展父类的功能,但不能改变父类原有的功能,换句话说,子类继承父类时,除了添加新的方法完成新增功能外,尽量不要重写父类的方法.
如果通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特比偶尔是运用多态比较频繁时,程序运行出错的概率会非常大.
面向对象中的继承性思考:
- 继承包含这样一层含义:父类中凡是已经实现好的方法, 实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。
- 继承在给程序设计带来便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障
- 问题提出:在编程中,如何正确的使用继承? => 里氏替换原则
里氏替换原则基本介绍:
- 里氏替换原则(Liskov Substitution Principle)在1988年,由麻省理工学院的以为姓里的女士提出的。
- 如果对每个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。换句话说,所有引用基类的地方必须能透明地使用其子类的对象。
- 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法
- 里氏替换原则告诉我们,继承实际上让两个类耦合性增强了, 在适当的情况下,可以通过聚合,组合,依赖 来解决问题
我们也可以通过提升的方法,来尽量满足里氏替换原则,假设现在有两个类,A 类和 B 类,如果 B 类继承 A 类,需要重写 A 类中的某些方法,那么,我们在 A 类 和 B 类之上,再抽取出一个更加通用的父类 Base,让 A 类和 B 类同时去继承 Base,这样 B 类就无须重写 A 类中的某些方法,达到基类的引用对子类对象透明的效果
代码一:
未遵循里氏替换原则,由于子类 B 继承父类 A 时重写了 func1() 方法,导致程序中使用多态时,本意是想调用重写前的方法,结果变成了重写后的方法,所以程序输出结果和预期不同
public interface Quadrilateral { double getLength(); double getWidth(); }
public class Rectangle implements Quadrilateral{ private double length; private double width; public void setLength(double length) { this.length = length; } public void setWidth(double width) { this.width = width; } @Override public double getLength() { return length; } @Override public double getWidth() { return width; } }
public class Square implements Quadrilateral { private double side; public void setSide(double side) { this.side = side; } @Override public double getLength() { return side; } @Override public double getWidth() { return side; } }
public class Client { public static void main(String[] args) { Rectangle r = new Rectangle(); r.setLength(20); r.setWidth(10); resize(r); print(r); } public static void resize(Rectangle r) { while (r.getWidth() <= r.getLength()) { r.setWidth(r.getWidth() + 1); } } public static void print(Quadrilateral q) { System.out.println(q.getLength()); System.out.println(q.getWidth()); } }
依赖倒转原则:
依赖倒转(Dependence Inversion Principle )原则是指:
- 高层模块不应该依赖低层模块,二者都应该依赖其抽象
- 抽象不应该依赖细节,细节应该依赖抽象
- 依赖倒转(倒置)的中心思想是面向接口编程
- 依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在java中,抽象指的是接口或抽象类,细节就是具体的实现类
- 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成
public interface HardDisk { /** * 存储数据 * @param data */ void save(String data); /** * 获取数据 * @return */ String getData(); }
public interface Cpu { /** * 运行cpu */ void run(); }
public interface Memory { /** * 保存 */ void save(); }
创建实现类实现业务
public class XiJieHardDisk implements HardDisk{ @Override public void save(String data) { System.out.println("使用希捷硬盘存储数据为:" + data); } @Override public String getData() { System.out.println("使用希捷硬盘读取数据"); return "数据"; } }
public class IntelCpu implements Cpu{ @Override public void run() { System.out.println("使用inter处理器"); } }
public class JinstonMemory implements Memory{ @Override public void save() { System.out.println("使用金士顿内存条"); } }
组装接口即可
@Getter @Setter public class Computer { private HardDisk disk; private Cpu cpu; private Memory memory; public void run() { System.out.println("运行计算机"); String data = disk.getData(); System.out.println("从硬盘上读取的数据是:" + data); cpu.run(); memory.save(); } }
public class ComputerDemo { public static void main(String[] args) { //创建组件 HardDisk hardDisk = new XiJieHardDisk(); Cpu cpu = new IntelCpu(); Memory memory = new JinstonMemory(); //创建计算机 Computer computer = new Computer(); //组装计算机 computer.setDisk(hardDisk); computer.setCpu(cpu); computer.setMemory(memory); computer.run(); } }
如果后续要替换硬件,可以创建好新的硬件实现接口功能,然后在Computer替换变量即可
接口隔离原则:
Interface Segregation Principle
基本介绍:客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上
举例说明:
- 类A通过接口Interface1依赖类B,类C通过接口Interface1依赖类D,如果接口Interface1对于类A和类C来说不是最小接口,但是类B和类D必须去实现他们不需要的方法。
- 按隔离原则应当这样处理:将接口Interface1拆分为独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则
问题代码:
public interface SafetyDoor { //防盗 void antiTheft(); //防火 void fireProof(); //防水 void waterProof(); } //实现功能 public class HeiSafetyDoor implements SafetyDoor{ @Override public void antiTheft() { System.out.println("防盗"); } @Override public void fireProof() { System.out.println("防火"); } @Override public void waterProof() { System.out.println("防水"); } } //程序开始,开始使用安全门 public class Client { public static void main(String[] args) { HeiSafetyDoor door = new HeiSafetyDoor(); door.antiTheft(); door.fireProof(); door.waterProof(); } }
此时:如果我另一个类,不想实现其中的功能,就不得不实现了,所以要把接口隔离开
优化代码:
//防盗接口 public interface AntiTheft { //防盗 void antiTheft(); } //防火接口 public interface FireProof { //防火 void fireProof(); } //防水接口 public interface WaterProof { //防水 void waterProof(); } //实现我需要的功能接口即可 public class HeiSafetyDoor implements AntiTheft,FireProof,WaterProof{ @Override public void antiTheft() { System.out.println("防盗"); } @Override public void fireProof() { System.out.println("防火"); } @Override public void waterProof() { System.out.println("防水"); } } //这个实现类我只需要两个功能,那我就不需要实现别的接口就好了 public class RedSafetyDoor implements AntiTheft,FireProof{ @Override public void antiTheft() { System.out.println("防盗"); } @Override public void fireProof() { System.out.println("防火"); } } //程序启动,开始使用安全门 public class Client { public static void main(String[] args) { HeiSafetyDoor door = new HeiSafetyDoor(); door.antiTheft(); door.fireProof(); door.waterProof(); System.out.println("============"); //创建另一个安全门对象 RedSafetyDoor redSafetyDoor = new RedSafetyDoor(); redSafetyDoor.antiTheft(); redSafetyDoor.fireProof(); } }
迪米特法则
迪米特法则的基本介绍
-
一个对象应该对其他对象保持最少的了解
-
类与类关系越密切,耦合度越大
-
迪米特法则(Demeter Principle)又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public 方法,不对外泄露任何信息
-
迪米特法则还有个更简单的定义:只与直接的朋友通信
//明星 @Getter @Setter @AllArgsConstructor @NoArgsConstructor public class Star { private String name; } //粉丝 @Getter @Setter @AllArgsConstructor @NoArgsConstructor public class Fans { private String name; } //公司 @Getter @Setter @AllArgsConstructor @NoArgsConstructor public class Company { private String name; } @Data public class Agent { private Star star; private Fans fans; private Company company; //和粉丝见面 public void meeting() { System.out.println(star.getName() + "和" + fans.getName() + "见面"); } //和公司洽谈 public void business() { System.out.println(star.getName() + "和" + company.getName() + "洽谈"); } } public class Client { public static void main(String[] args) { //创建经纪人 Agent agent = new Agent(); //创建明星 Star star = new Star("林青霞"); //创建粉丝 Fans fans = new Fans("李四"); //创建公司 Company company = new Company("苹果"); agent.setStar(star); agent.setFans(fans); agent.setCompany(company); agent.meeting(); agent.business(); } }
合成复用原则
合成复用原则(Composite Reuse Principle)
原则是尽量使用合成/聚合的方式,而不是使用继承,即尽量使用 has a 的关系,而不要使用 is a 的关系
标签:原则,void,七大,System,class,println,设计模式,public,out From: https://www.cnblogs.com/lpzjava/p/16619056.html