常用的设计原则和设计模式
1****常用的设计原则(记住)
1.1****软件开发的流程
需求分析文档、概要设计文档、详细设计文档、编码和测试、安装和调试、维护和升级
1.2****常用的设计原则
(1)开闭原则(Open Close Principle)
对扩展开放对修改关闭,为了使程序的扩展性好,易于维护和升级。
(2)里氏代换原则(Liskov Substitution Principle)
任何基类可以出现的地方,子类一定可以出现,多使用多态的方式。
(3)依赖倒转原则(Dependence Inversion Principle)
尽量多依赖于抽象类或接口而不是具体实现类,对子类具有强制性和规范性
(4)接口隔离原则(Interface Segregation Principle)
尽量多使用小接口而不是大接口,避免接口的污染,降低类之间耦合度。
(5)迪米特法则(最少知道原则)(Demeter Principle)
一个实体应当尽量少与其他实体之间发生相互作用,使系统功能模块相对独立。
高内聚,低耦合。
分析:
问题1:如何理解"高内聚,低耦合"?
我给大家举一个现实生活的例子吧,好吧,比如说我是java讲师。
意味着讲师应该具备的所有的技能,我是不是应该高度聚集于我一体?对吧,讲师要能讲课,讲师要能研发,讲师要能解决学员的问题,要能答疑。啊,讲师要能调试程序的bug。对吧,这些哎,该有的技能,我是不是要高度聚集于我一体,这就叫高内聚。
对吧哎,然后呢?低偶合什么意思?就是说我不该有的跟讲师无关的,那些工作内容我是不是应该减少关联啊?或者说关联的是不是越少越好?对吧,比如说跟什么呢?跟咱们啊行政的啊,老师之间的这个关联度是不是应该或者说他们的工作内容是不是这个关联度应该越少越好
(6)合成复用原则(Composite Reuse Principle)
尽量多使用合成/聚合的方式,而不是继承的方式。
分析:
问题2:如何理解合成复用原则?
所以大家以后呢,只要涉及当前类使用其他类就用合成复用原则。这个手法我们用的是非常多的。
2****常用的设计模式
2.1****基本概念
(1)设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
分析:
问题3:如何理解设计模式?
前人总结了很多的经验而把这些经验呢归纳起来,实际上就变成了设计模式。
(2)设计模式就是一种用于固定场合的固定套路。
2.2****基本分类
(1)创建型模式 - 单例设计模式、工厂方法模式、抽象工厂模式、...
(2)结构型模式 - 装饰器模式、代理模式、...
(3)行为型模式 - 模板设计模式、...
3****设计模式详解(重点)
3.1****单例设计模式
单例设计模式主要分为:饿汉式 和 懒汉式,懒汉式需要对多线程进行同步处理。
分析:
问题4:懒汉式线程如何同步实现?
那上面的问题应该如何解决呢?
那这个时候是不是就意味着我们需要对getInstance这个方法的?这种调用是不是要进行一个什么?线程同步的处理。也就是说我们在执行这段代码的时候,我们是不是应该是有且只能有一个线程进来执行,其他线程靠边。
这两种方式实际上是不是就是等价的一个效果。
问题5:懒汉式的线程同步为什么要优化以及如何优化?
但是这种写法啊,虽然能够实现线程同步的效果,但是还不够完美。哪块儿不太完美呢?是因为我们知道只有第一次调用get instance的方法的时候,我们是不是才需要把这段代码锁起来?对吧,而且的话实际上是new对象的这段代码是不是锁起来就够了,
往后我们第二次第三次甚至再往后再调get instance这个方法的时候还用得着再锁起来吗?不用,是不是直接把这个对象return出去就行?
对吧,那如果说每次调这个方法都是加锁,然后判断返回对象再解锁,然后再加锁,然后判断。然后返回对象再解锁,是不是就有点啰嗦了?是不是所以我们把这段代码还可以,怎么办?做一个简单的优化。
3.2****普通工厂模式
( 1 )基本概念
普通工厂方法模式就是建立一个工厂类,对实现了同一接口的不同实现类进行实例的创建。
分析:
问题6:如何理解工厂类?
那以前呢,都是让我们自己直接去new,现在我们自己都不用new了,把new对象的活儿。直接委派给一个工厂类了。明白这意思吧,也就是说通过工厂类来实现批量对象的生产。
( 2 )类图结构
分析:
那在这张图里面,其实大家可以看到这里面是不是有很多的方框啊?对不对?比如说这里面的方框都是什么意思?这里面的每一个方框实际上就是一个类或者接口的。明白这意思吧,哎,然后呢?
而且呢?在这个方框里面,其实大家会发现是不是有两条横线,两条横线,其实就是把这个方框的内容分成了几部分?三部分内容。上面的呢叫类名,然后中间的呢是这个类的成员变量,因为我们当前这个类的成员变量没有,所以这儿就空着没有写。明白这意思吧,然后下面是什么呢?下面是这个类的成员方法,然后方法名叫什么呢?叫produce这个方法呢?加号代表的是public修饰的。
然后呢?这个方法的返回值类型呢?是sender类型。然后这个方法的参数呢?是string类型的type。
清楚了吗?也就是说这个类图是不是够详细的了,因为它已经告诉你这个类叫什么,这个类里面有哪些成员变量都什么类型的,这个类中有哪些成员方法,方法的返回值类型参数?等等,是不都已经给我们标出来了。
上面的图转成代码:
接口:
接口实现类:
接口实现类:
工厂类(通过工厂类来实现批量对象的生产):
测试类:
问题7:普通工厂模式的意义是什么?
也就是说在以后的开发中,当我们需要涉及到大量对象的创建。而且的话呢,还想让它的维护性,扩展性更好一些,那我们是不是就可以写一个工厂类。
( 3 )主要缺点
在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,并且可能出现空指针异
常。
问题:8这个普通工厂方法模式里面呢?也有一个小小的问题,什么问题呢?
原来我是一个produce方法,然后根据参数传入的字符串来决定创建什么样的对象,并返回。
但是现在在这个工厂类中提供了两个工厂方法,一个是创建mail对象的。一个是用来创建sms对象的,不需要再传字符串了。避免了字符串的传入也就避免了空指针异常的发生。
那么在刚刚的这个案例代码中啊,我们说多个工厂方法模式比我们的普通工厂模式。比较有优势的一点在于什么地方?就是我们直接可以调用方法来创建具体的对象;不需要是不是再传字符串了,那不需要传字符串,是不是也就避免了空指针异常的发生。
3.3****多个工厂方法模式
( 1 )类图结构
( 2 )主要缺点
在多个工厂方法模式中,为了能够正确创建对象,先需要创建工厂类的对象才能调用工厂类中的生
产方法。
3.4****静态工厂方法模式
但是接下来我又有一个问题,请思考,请问上面这一行代码是干嘛的呀?
问题的具体体现:
是为了能够拿到这个类的引用点是不是去调用produce mail方法
那也就是说创建工厂类对象的价值仅仅是为了调用生产方法呗
所以说多个工厂方法模式还是不够优秀。也就是说多个工厂方法模式的话呢,实际上也有一个缺点,就是为了能够创建对象,我们得创建工厂类的对象,也就是说得创建sender factory这个类的对象之后。拿着这个类的引用,是不是才能调用produceMail?produceSms这样的方法,否则这俩方法是不是调用不了。
那现在我们就反推一下,我们能不能在调用这俩方法的时候,不需要创建这个类的对象。
可以,只需要给这两个方法变成什么来着,加上static关键字变成静态方法,隶属于类层级,是不是就可以直接用类名点调用。
所以我们又一次改进,提出了静态工厂方法模式
( 1 )类图结构
注:这里的SendFactory类和上面的不同之处是加了下划线(下划线就代表静态)
( 2 )实际意义
工厂方法模式适合:凡是出现了大量的产品需要创建且具有共同的接口时,可以通过工厂方法模式
进行创建。
( 3 )主要缺点
工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序生产新的产
品,就必须对工厂类的代码进行修改,这就违背了开闭原则。
因为我们现在的这个工厂类里面是不是只能创建MailSender对象和SmsSender对象。假设有一天客户提出新的需求,除了创建上面两个对象外,我还需要创建一个packet Sender类型对象。请问怎么做?
很简单呀,只需要在sender factory类里面增加一个叫producedPacket的方法不就可以了,这样做是可以的,但是这样做会违背我们讲过的开闭原则。还记得什么叫开闭原则吗?开闭原则的意思是对扩展开放,对修改关闭。也就是说,一旦我们一段代码写完之后,测试没问题,之后是不是尽量不要对这个原有的代码做任何的改变或者修改。那现在的话,我们如果增加方法,不就是对工厂类做修改了吗?
那有没有办法解决呀?可以我们又提出了另外一个模式,叫做抽象工厂模式
3.5****抽象工厂模式
( 1 )类图结构
所以一定要注意静态工厂模式和抽象工厂模式的最大的区别就是原来是一个工厂类,两个生产方法,现在变成了两个工厂类,各自有一个生产方法。
这样改进跟前面这种方式相比呢,有什么优势吗?
优势就在于如果有一天我们想要生产新的产品,比如说packetSender类,我们就可以在接口Provider中增加一个类。
那这样一来,原有的代码是不是一个字儿都不用变。也就意味着不用违背开闭原则了。
接口:
接口实现类1:
接口实现类2:
测试类:
那么介绍完抽象工厂模式之后啊,我们强调了它的一个理念就在于。如果我想要生产新的产品的时候,是不是它更有优势,而且不用违背开闭原则好,
那接下来我们就把这个代码写一写,比如说我现在想要增加一个PacketSender对象的发送,那问大家怎么办?
思路:
3.6****装饰器模式
( 1 )基本概念
装饰器模式就是给一个对象动态的增加一些新功能,要求装饰对象和被装饰对象实现同一个接口,
装饰对象持有被装饰对象的实例。
( 2 )类图结构
分析:大家会发现这儿有一个接口叫Sourceable的接口,里面是不是有一个叫method的抽象方法。 然后下面有俩实现类,一个是Source类,那这个source类啊,其实就是被装饰类,它实现Sourceable 接口。重写method方法明白了吧?后面呢,还有一个叫decorator,这么一个类,这个类呢,也是实现Sourceable 接口。然后那这个类是干嘛的呢?是装饰类,也就是这个类的价值是为了吧?对source被装饰类进行装饰的。明白了吧,然后它里面呢有一个成员变量是一个source able类型的source, 然后减号的意思是私有的成员变量嘛,然后因为实现了这个接口,当然也得重写method方法。能看明白这个类图的结构了吧。
通过运算结果我们知道:在保证原有功能不变的情况下,你看原有功能不变,我是不是又增加了一个新的功能,这就叫装饰模式。
以大家以后只要涉及到动态增加功能。我们就用装饰器模式。
(3)实际意义
(3.1)可以实现一个类功能的扩展。
(3.2)可以动态的增加功能,而且还能动态撤销(继承不行)。
(3.3)缺点:产生过多相似的对象,不易排错。
3.7****代理模式
( 1 )基本概念
(1.1)代理模式就是找一个代理类替原对象进行一些操作。
(1.2)比如我们在租房子的时候找中介,再如我们打官司需要请律师,中介和律师在这里就是我们的代理。
( 2 )类图结构
也就是说这里面我可以直接new一个对象跟我们的装饰器模式做一个对比
装饰器模式是通过构造方法把这个原始对象是不是传过来?因为装饰嘛,我要在原有对象的基础上进行。但是代理模式实际上是不是就全权创建对象就可以了?还用再传过来吗?不用。我直接是不是自个new就完事了。
( 3 )实际意义
(3.1)如果在使用的时候需要对原有的方法进行改进,可以采用一个代理类调用原有方法,并且对产生的 结果进行控制,这种方式就是代理模式。
(3.2)使用代理模式,可以将功能划分的更加清晰,有助于后期维护。
( 4 )代理模式和装饰器模式的比较
( 4.1)装饰器模式通常的做法是将原始对象作为一个参数传给装饰者的构造器,而代理模式通常在一个代理类中创建一个被代理类的对象。
(4.2)装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。
3.8****模板方法模式
( 1 )基本概念
(1)模板方法模式主要指一个抽象类中封装了一个固定流程,流程中的具体步骤可以由不同子类进行不同的实现,通过抽象类让固定的流程产生不同的结果。
( 2 )类图结构
测试类:
( 3 )实际意义
将多个子类共有且逻辑基本相同的内容提取出来实现代码复用。
不同的子类实现不同的效果形成多态,有助于后期维护。