怎么选择郑州网站建设,公司邮箱后缀正确的是以下,wordpress修改自定义尺寸logo,wordpress 新浪微博分享文章目录设计模式七大设计原则开闭原则里氏替换原则依赖倒置原则接口隔离原则迪米特法则-最少知道原则单一职责原则合成复用原则设计模式
面向对象的三个基本特征#xff1a;
继承封装多态
设计模式体现了代码的耦合性、内聚性、可维护性、可扩展性、重用性、灵活性。
代码…
文章目录设计模式七大设计原则开闭原则里氏替换原则依赖倒置原则接口隔离原则迪米特法则-最少知道原则单一职责原则合成复用原则设计模式
面向对象的三个基本特征
继承封装多态
设计模式体现了代码的耦合性、内聚性、可维护性、可扩展性、重用性、灵活性。
代码重用性相同功能代码不用多次编写可读性可扩展性添加新功能非常方便可维护可靠性当我们添加新功能不影响原有的功能使程序呈现高内聚、低耦合的特性
七大设计原则
开闭原则
原则一个软件实体如类、模块、函数应该对拓展开发、对修改关闭
在程序需要拓展的时候不能去修改原有代码为了易于维护我们应该更多的学会使用接口和抽象类在设计的时候尽量适应变化提高系统的稳定性、灵活性。
场景书店销售书籍
public class OpenClosePrinciple {interface Book{String getName();double getPrice();String getAuthor();}class NovelBook implements Book{private String name;private String author;private double price;public NovelBook(String name, String author, double price) {this.name name;this.author author;this.price price;}Overridepublic String getName() {return this.name;}Overridepublic double getPrice() {return this.price;}Overridepublic String getAuthor() {return this.author;}}Testvoid client(){NovelBook novelBook new NovelBook(斗罗大陆, 唐家三少, 55.8);System.out.println(novelBook.getName() novelBook.getAuthor() novelBook.getPrice());}}假如我们想在双十一对书籍进行打折。
实现方案 修改接口在Book类中新增一个打折接口但是这样做其他所有实现类都需要改动。 修改实现类方法NovelBook类修改原有的getPrice()方法实现打折逻辑但是这样做的话违背了开闭原则如果双十一过去了是不是还有再把代码回退 实现类新增方法NovelBook类新增getDiscountPrice()方法这个看起来不错但是一个类中提供两个获取价格的接口对调用者不是很友好而且随着业务需求越来越多这个类不断添加新的方法本来我们设计这个类的目的只是想获取书籍的基本信息但是现在又混合了很多业务逻辑那么这个类就违背了单一职责原则。 派生出一个打折类该类继承NovelBook基类用来专门处理打折逻辑这样不用修改原有实现类不会对上层调用者产生影响利用拓展实现功能其实这种对基类已实现的方法进行覆盖违背了里氏替换原则如果想不违背里氏替换原则只能在派生类中添加新的方法并且在基类的方法中添加final保证不会被重写这里我们只是讲解开闭原则就不考虑那么多。 class DiscountNovelBook extends NovelBook{public DiscountNovelBook(String name, String author, double price) {super(name, author, price);}Overridepublic double getPrice() {System.out.println(打折后价格);return super.getPrice() * 0.88;}}Testvoid client(){//老的业务逻辑正常调用NovelBook novelBook new NovelBook(斗罗大陆, 唐家三少, 55.8);System.out.println(novelBook.getName() novelBook.getAuthor() novelBook.getPrice());System.out.println(----------------);//新促销业务调用打折代码DiscountNovelBook discountNovelBook new DiscountNovelBook(斗罗大陆, 唐家三少, 55.8);System.out.println(discountNovelBook.getName() discountNovelBook.getAuthor() discountNovelBook.getPrice());}引用https://www.jianshu.com/p/d36da4f136c4
里氏替换原则
原则所有基类在的地方都可以换成子类程序还可以正常运行。这个原则与继承特性密切相关。
相信我们经常用到继承那你知道继承有哪些优点
子类拥有父类的所有方法和属性从而减少创建类的工作量。提高了代码的重用性。提高了代码的拓展性子类不但拥有父类的所有功能还可以添加自己的功能。
缺点
继承是有侵入性的只要继承就必须拥有父类的所有属性和方法。降低了代码的灵活性。因为继承时父类对子类有一种约束。增强了耦合性当需要对父类的代码进行修改时必须考虑到对子类产生的影响。
里氏替换原则对继承进行了规则上的约束
子类必须实现父类的抽象方法但不得重写(覆盖)父类的已实现的方法。子类可以增加自己特有的方法。当子类重载父类的方法时方法的形参要比父类的输入参数更宽松。(只能重载不能重写)当子类的方法实现父类的抽象方法时方法的返回值要比父类更严格。
约束详细解释 子类必须实现父类的抽象方法但不得重写(覆盖)父类的已实现的方法。 首先子类必须实现父类的抽象方法不然编译无法通过 如果重写父类的方法当用子类替代父类后会出现意想不到的错误如果想避免这种问题我们可以将父类不得重写的方法添加final关键字这样在语法层面就可以避免违反里氏替换原则 子类可以增加自己特有的方法 子类可以对父类功能进行拓展。 当子类重载父类的方法时方法的形参要比父类的输入参数更宽松 定义一个父类Father#run(Map map)利用子类重载run方法Son#run(HashMap map)如果此时HashMap map new HashMap()作为参数传入son.run(map) public class LiskovSubPrinciple {public class Parent {void run(Map map){System.out.println(父类执行...);};}public class Son extends Parent{void run(HashMap map){System.out.println(子类执行...);}}Testvoid run(){HashMapObject, Object map new HashMap();Parent parent new Parent();parent.run(map);Son son new Son();son.run(map);}//结果//父类执行...//子类执行...
}结果会发现用同一个参数子类和父类调用结果却不同会出现程序调用混乱由于子类形参为HashMap导致父类方法没有被重写的情况下调用了子类。 如果我们把子类的形参范围设置的比父类更大就不会出现上述问题。 public class LiskovSubPrinciple2 {public class Parent {void run(HashMap map){System.out.println(父类执行...);};}public class Son extends Parent{void run(Map map){System.out.println(子类执行...);}}Testvoid run(){HashMapObject, Object map new HashMap();Parent parent new Parent();parent.run(map);Son son new Son();son.run(map);}//父类执行...//父类执行...
}这次会发现两次调用结果一致。 当子类的方法实现父类的抽象方法时方法的返回值要比父类更严格 如果子类的返回值类型比父类范围更广编译无法通过。
依赖倒置原则
原则面向接口编程不要面向实现。
场景小明去不同的商店购物电器但是商店有很多种
public class DependenceInversionPrinciple {public interface Shop{void sell();}public class GomeShop implements Shop{Overridepublic void sell() {System.out.println(国美商店...);}}public class SuningShop implements Shop{Overridepublic void sell() {System.out.println(苏宁易购...);}}public class People{String name;public People(String name) {this.name name;}void shopping(Shop shop){System.out.print(this.name);shop.sell();}}Testvoid testMain(){People people new People(小明购物:);people.shopping(new GomeShop());people.shopping(new SuningShop());/*小明购物:国美商店...小明购物:苏宁易购...*/}
}
如果将方法形参的Shop接口改成实现类需要新增很多种方法不符合依赖倒置原则。
接口隔离原则
原则使用多个隔离的接口比使用单一接口更好。
场景现在有一个接口声明了1、2、3、4这四个方法A类需要用到1、2方法B类需要用到3、4方法C类需要用到1、2、3、4方法。
public class InterfaceSegregationPrinciple {public interface I{void method1();void method2();void method3();void method4();}/*** A类需要用到方法1 2*/public class A implements I{Overridepublic void method1() {System.out.println(实现方法1);}Overridepublic void method2() {System.out.println(实现方法2);}Overridepublic void method3() {//不实现}Overridepublic void method4() {//不实现}}public class B implements I{Overridepublic void method1() {//不实现}Overridepublic void method2() {//不实现}Overridepublic void method3() {System.out.println(实现方法3);}Overridepublic void method4() {System.out.println(实现方法4);}}public class C implements I{Overridepublic void method1() {System.out.println(实现方法1);}Overridepublic void method2() {System.out.println(实现方法2);}Overridepublic void method3() {System.out.println(实现方法3);}Overridepublic void method4() {System.out.println(实现方法4);}}
}
可以看出A和B类存在很多空方法这种方式对调用者来说不是很友好并且接口很臃肿不符合接口隔离原则。
让我们重新设计一下
public class InterfaceSegregationPrinciple2 {public interface I{void method1();void method2();}public interface K{void method3();void method4();}/*** A类需要用到方法1 2*/public class A implements I{Overridepublic void method1() {System.out.println(实现方法1);}Overridepublic void method2() {System.out.println(实现方法2);}}public class B implements K{Overridepublic void method3() {System.out.println(实现方法3);}Overridepublic void method4() {System.out.println(实现方法4);}}public class C implements I,K{Overridepublic void method1() {System.out.println(实现方法1);}Overridepublic void method2() {System.out.println(实现方法2);}Overridepublic void method3() {System.out.println(实现方法3);}Overridepublic void method4() {System.out.println(实现方法4);}}
}将接口拆分为两个接口这样每个类只需要实现方法不需要空方法符合接口隔离原则。
迪米特法则-最少知道原则
一个对象应该对其他对象有最少的了解。
原则只和直接的朋友交流、减少对朋友的了解
概念
直接朋友成员变量、方法入参、返回参数例如A类方法参数是B类对象、返回值是C类对象则A与B、C是直接朋友。
朋友两个对象耦合就会成为朋友例如A类中方法代码块中使用了B类对象AB是朋友。 只和直接的朋友交流
应用场景
现在有三个角色老师Teacher、班长GroupLeader、学生Student老师让班长去统计全班人数。
实现一
public class DemeterPrinciple {private static class Teacher{/*** 发号命令* param groupLeader*/int command(GroupLeader groupLeader){//创建学生数组Student[] students new Student[20];for (int i 0; i 20; i){students[i] new Student(i);}//让班长统计人数return groupLeader.count(students);}}private static class GroupLeader{int count(Student[] students){return students.length;}}private static class Student{int id;public Student(int id) {this.id id;}}public static void main(String[] args) {Teacher teacher new Teacher();int total teacher.command(new GroupLeader());System.out.println(人数: total);}
}让我们来分析一下上面代码
Teacher类朋友关系GroupLeader(直接朋友)、Student(朋友)
由于Teacher类中和非直接朋友有关系这就违背了demeter法则。方法是类中的一个行为类竟然不知道自己的行为与其他类产生依赖关系这严重违反了迪米特法则。
让我们做如下修改
public class DemeterPrinciple2 {private static class Teacher{/*** 发号命令* param groupLeader*/int command(GroupLeader groupLeader){//让班长统计人数return groupLeader.count();}}private static class GroupLeader{Student[] students;public GroupLeader(Student[] students) {this.students students;}int count(){return students.length;}}private static class Student{int id;public Student(int id) {this.id id;}}public static void main(String[] args) {Teacher teacher new Teacher();//创建学生数组Student[] students new Student[20];for (int i 0; i 20; i){students[i] new Student(i);}int total teacher.command(new GroupLeader(students));System.out.println(人数: total);}
}再看看Teacher、GroupLeader类中不依赖朋友而只有直接朋友类之间解耦了。 减少对朋友的了解
尽量减少一个类对外暴露的方法。
场景人使用咖啡机获得咖啡。
public class DemeterPrinciple3 {private static class People{private CoffeeMachine coffeeMachine;public People(CoffeeMachine coffeeMachine) {this.coffeeMachine coffeeMachine;}void takeCoffee(){coffeeMachine.addCoffeeBean();coffeeMachine.addWater();coffeeMachine.makeCoffee();}}private static class CoffeeMachine{public void addCoffeeBean(){System.out.println(添加咖啡豆);}public void addWater(){System.out.println(加水);}public void makeCoffee(){System.out.println(制作咖啡);}}public static void main(String[] args) {CoffeeMachine coffeeMachine new CoffeeMachine();People people new People(coffeeMachine);people.takeCoffee();}}上述代码中People类虽然只有一个CoffeeMachine类的直接朋友但是他对这个朋友过多的了解其实我们不用关系怎么去制作咖啡过程结合现实中的例子拿到我们使用咖啡机需要按三个按钮才能出咖啡如果咖啡机底层逻辑发生改变我们人还要去按更多的按钮
让我们做下优化
public class DemeterPrinciple4 {private static class People{private CoffeeMachine coffeeMachine;public People(CoffeeMachine coffeeMachine) {this.coffeeMachine coffeeMachine;}void takeCoffee(){coffeeMachine.makeCoffee();}}private static class CoffeeMachine{private void addCoffeeBean(){System.out.println(添加咖啡豆);}private void addWater(){System.out.println(加水);}private void doMakeCoffee(){System.out.println(制作咖啡);}public void makeCoffee(){addCoffeeBean();addWater();doMakeCoffee();}}public static void main(String[] args) {CoffeeMachine coffeeMachine new CoffeeMachine();People people new People(coffeeMachine);people.takeCoffee();}}
这样修改以后人不用去关系咖啡机的制作过程只需要咖啡机提供的一个按钮就可以喝到美美的咖啡啦 引用https://www.jianshu.com/p/4b244f132439 单一职责原则
原则一个类只负责一项职责同样适用于方法
通常情况下我们应该遵守单一职责原则只有当逻辑足够简单可以通过在类中添加不同方法违反单一职责原则。
public class SingleResponsibilityPrinciple {/*** 交通工具类* 逻辑简单直接在类中添加方法即可*/class Vehicle{void run(String car){System.out.println(car run);}void fly(String airplane){System.out.println(airplane fly);}}Testvoid testRun(){Vehicle vehicle new Vehicle();vehicle.run(奔驰);vehicle.fly(播音747);}
}
合成复用原则
原则将已有的对象加入新对象中作为新对象的成员对象来实现新对象可以调用已有对象的功能达到复用。 在java中优先使用组合而不是继承。如果要使用继承关系必须严格遵循里氏替换原则而合成复用原则同里氏替换原则相辅相成的两者都是开闭原则的具体实现。 复用分为继承复用、合成复用
继承复用缺点
会破坏类的封装性继承会将父类的实现细节暴露给子类又称“白箱复用”。父类与子类耦合度高父类发生修改会影响子类功能不利于类的拓展与维护。限制了复用的灵活性。从父类继承而来的实现是静态的在编译时已经定义运行时不能发生变化。
采用组合复用优点将已经有对象作为成员对象成为新对象的一部分
维护了类封装性成员对象不会暴露实现细节这种方式称为“黑箱复用”新旧类耦合度低只需要利用成员对象接口访问。复用的灵活性这种复用可以在运行时动态进行新对象可以动态地引用于成员对象类型相同的对象。
场景汽车按照动力分为汽油汽车、电动汽车等颜色有红色、黑色、白色等如果同时考虑这两种组合我们应该怎么实现
如果利用继承方式 利用继承方式会出现多个子类随着汽车种类越多子类会膨胀并且每添加一个颜色都得继承汽油、电动这两个类都要修改源代码如果利用组合可以避免这种问题。 引用http://c.biancheng.net/view/1333.html