设计模式desine pattern梳理
1、不管是逆向破解,还是正向开发,看别人的代码是必不可少的。如果别人的代码看起来特别费劲,甚至完全看不懂,原因如下:
- 正向开发做的太少,不了解业务需求,不知道代码的业务意义
- 数据结构和算法技能缺失
- 完全不懂设计模式,不知道代码分层的意义
本文来总结一下常见的设计模式场景!
2、(1)长时间用java、c++等Object Oriented高级语言做软件开发的人员肯定都有过这样的体会:各种不同的业务场景都有不同的类设计方式!比如A是interface类,定义了一些function,但是没具体实现。B、C来implement A类的方法,然后D又extends B类,那么问题来了:为啥要搞这么复杂的类关系(又是extends,又是implements的)?为啥不直接把所有的功能都在A里面实现了?衍生出B/C/D这么多类,这不是脱了裤子放屁么?后续的运维会不会很复杂?
先说说自己的理解:所谓的设计模式,本质就是把常用的业务场景抽象成现成的代码模块,开发人员遇到响应的业务场景后可以直接简单粗暴地酌情修改模板即可!由于常见的业务常见有20多个,所以设计模式大概也有20多个!
(2)正式介绍设计模式前,先说说java三大特性: 封装Encapsulation、继承Inheritance、多态Polymorphism!
- 封装:核心目的就是把业务上相关的属性聚集在一起,然后通过方法去处理这些属性!这也是普通类的定义思路,比如person类,有成员变量name、age等,也有成员函数getName、getAge、setName、setAge等操作成员变量的方法!第三方直接通过成员函数去读写成员变量!
- 继承: 通过extends或implements实现,本质就是增量开发,或者说是复用,避免重复造轮子!父类定义了很多成员变量或方法,子类在继承后可以直接拿过来用了! 子类不需要修改原有的代码,因此不会给原有代码带来新的 BUG,也不用因为对原有代码的修改而重新进行测试!当然子类也可以添加自己个性化的属性和方法!
- 多态: 同一种类型(比如父类的引用),不同的表现形式(比如父类引用指向不同子类的instance,不同子类的instance根据自己的业务特性overwrite了父类的方法,所以才有不同的表现形式)!多态最直接的好处:消除了类之间的耦合关系,使程序更容易扩展!说明的demo代码如下:
/** * 汽车接口 */ interface Car { // 汽车名称 String getName(); // 获得汽车售价 int getPrice(); } // 宝马 class BMW implements Car { public String getName() { return "BMW"; } public int getPrice() { return 300000; } } // 奔驰 class BENZ implements Car { public String getName() { return "BENZ"; } public int getPrice() { return 400000; } } // 汽车出售店 public class CarShop { // 售车收入 private int money = 0; // 卖出一部车 public void sellCar(Car car) { System.out.println("车型:" + car.getName() + " 单价:" + car.getPrice()); // 增加卖出车售价的收入 money += car.getPrice(); } // 售车总收入 public int getMoney() { return money; } public static void main(String[] args) { CarShop carShop = new CarShop(); // 卖出一辆宝马 carShop.sellCar(new BMW()); // 卖出一辆奔驰 carShop.sellCar(new BENZ()); System.out.println("总收入:" + carShop.getMoney()); } }
比如父类定义了某个interface car类,不同车品牌的子类分别implements car类(当然要加上自己的业务特性)。要增加车类品牌,只需要新建类来implements 父类car就行了,现有的父类car和车品牌的代码完全不用改(尤其是sellCar方法,传入的参数是父类car,而没有执行特定的车品牌),实现标准的松耦合,扩展性非常好!理论上讲:这里通过泛型也能实现!这里多说几句:Java中对象的引用本质和c/c++的指针完全一样,存的都是地址。c/c++可以强制转换类型来匹配,Java不需要了,直接传入引用就行!从底层的技术原理讲:泛型和多态没有本质区别,传入的参数都是实例在heap的地址,而不是对象实例!
(3)由于java语言目前具备的这些特性,后续所有的设计模式都是基于这些特性实现的!这里面又涉及到两个很容易混淆的概念了:abstract和interface!详细对比如下:
因为abstract和interface都能被子类继承或实现,初学入门的开发人员大概率还是不懂哪些地方用abstract,哪些地方用interface!既然技术上无法区分,就只能从业务角度或设计理念考虑了!说人话就是Java的设计人员当初为啥既要推出interface,又要推出abstract了?
这里继续举例:总所周知,笔记本、台式机都属于PC范畴;PC和server都属于计算机范畴,所以笔记本、台式机、server其实都是计算机(因为都是冯诺依曼架构),所以这3者的基础功能都是一样的,只不过各自的特性不同,导致了应用场景不同。既然都是一类的,那么这3这就是“is a”的关系,具体实现时可以定义一个abstract compute类,类中定义一些这3者共用的属性和方法,然后laptop、desktop、server分别extends computer,分别加上自己的特性就行(也可以overwrite父类的方法)!
除了上述这些“大件”IT产品,手机也是冯诺依曼架构,基础功能和上述3类是一样的,所以手机也能extends computer类,但手机还能打电话、发短信,这个是上述3类不具备的,所以还可以单独定义一个interface,包含call、sendMsg方法,cellPhone来implements这个interface,实现call和sendMsg方法!如果还有不同的手机品牌,可以继续extends cellPhone类,然后加上不同品牌自己的特性!
这时候就直到extends或implements的好处了吧:额外增加了品类,只需要新增类就行了,完全不用改现有的代码,扩展性棒棒哒!
现在就可以回答文章开头的问题了:为啥刚开始要定义abstract或interface,然后让子类去extends或implements了?这样做产生了大量的类,后续维护代码的时候岂不是很困难了?这种分层解耦设计思路的核心优势如下:
- 不操心:每层都只需要关心自己的代码或业务逻辑是不是正确,做好“各扫门前雪”就好!父类的方法由其开发人员确保正确,子类的开发人员是不用关心过程的!
- 易扩展:
- 类和类之间松耦合,子类只需要添加自己的特性就行,完全不用改父类或兄弟类代码,容易扩展!
- 函数声明和实现时用父类的引用(本质就是个地址),函数调用传参时可以传子类的instance(的地址),不同子类的instance可以执行不同的代码,但是函数接口完全不用改,这就是多态最大的优点!
3、总结一下做开发代码层面的要点
- 4大bility:
(1)maintainablity可维护性:别人也能容易看懂自己写的代码;如果要修改功能,需要改动的地方越少越好,主要靠多态、chain of responsibilities、observer、meditor【链表遍历】实现!
(2)extensibility/scalablity可扩展性: 增加新功能,甚至都不需要修改以前的代码,主要靠多态、chain of responsibilities、observer、meditor【链表遍历】实现!
(3)reuseablity可复用性: 代码可在不同的地方复用,主要靠extends继承实现!
(4)flexibility/mobility/adaptablity灵活性:代码interface灵活调用,按需设置成public属性!
- 5大principle
(1)single responsibility principle :
-
- 一个类实现单一职责,相关的方法都在该类中聚合,能互相调用,这就是所谓的高内聚!
- 不同职责由不同的类实现,类和类之间尽量少调用接口,保持相对的独立性,避免A类改变后B类也必须改的尴尬局面,这就是所谓的低耦合!
(2)open-close principle:
-
- 尽量在不修改原来代码的情况下扩展,还是要依赖多态!函数调用传参时用父类的引用,但是可以传子类的instance;不同子类的instance实现不同的方法
(3)liscov substitution principle:
-
- 使用父类的地方可以透明使用子类,还是依靠多态!
(4)dependcy inverse priciple:
-
- 面向抽象编程,函数的参数尽量用抽象类,后续传入不同的子类来实现不同的功能,但是函数本身不需要改,和上面一样都依赖多态!
(5)interface segeneration principle:
-
- 每个接口承担一个职责,不同职责通过不同的接口实现,这点类似第一条;后续暴露接口给开发人员的时候可以只暴露必要的接口!
常见设计模式总结:
参考:
1、https://segmentfault.com/a/1190000009141566 java object oriented 封装、继承、多态特性
2、https://blog.csdn.net/weixin_44422604/article/details/107184591 interface和abstract对比
3、https://www.bilibili.com/video/BV1cU4y1a76S/ 马士兵设计模式