Java设计模式笔记一.单例模式1.单例模式(Singleton)表示一个类只能生成一个对象。
2.典型应用:Servlet就是使用的单例模式,不管多少个用户访问一个Servlet都是访问的一个Servlet对象。
3.对于单例模式实现的想法:1)首先明确生成一个类的对象时肯定要调用该类的构造方法。
2)那么我们必须要从构造方法入手解决一个类只能生成一个对象这一问题。
3)假设不提供构造方法,该类会默认是一个不带参数的构造方法,显然生成该类对象时还是会调用默认的构造方法,还是无法解决问题4)则我们肯定是要通过提供构造方法来解决这一问题,那么现在我们的问题是到底该提供怎样的构造方法呢?5)那么我们就想到一般的构造方法都是public的,在类的外部(其他类)可以调用该构造方法生成多个对象,显然是不行的,那么我们就想到private关键字,private表示只能类的内部才能访问。
6)那么现在我们想到可以把构造方法定义成一个私有(private)的,只有该类的内部才能访问,但是在类的外面不能生成对象了,这样成为零例了,与单例很接近了。
7)那么我们需要在该类中提供一个返回唯一一个该类的对象供外部调用。
但是现在我们构造方法外部都不能访问,没有对象该怎么访问这个类的指定方法呢?8)那么我们想到把我们提供的方法定义成静态方法(非实例方法),就可以直接通过该类加点号访问该类的该方法了。
9)那么我们可以举一个单例的例子了:class Singleton{private static Singleton singleton=new Singleton();private Singleton(){}public static Singleton getInstance(){return singleton;}}以上例子便是实现了单例模式,注意两个红色地方,静态的方法只能访问静态的属性。
在类的外部直接通过类名加点号getInstance()访问唯一一个对象了。
二.简单工厂模式1.简单工厂模式是类的创建模式,又叫做静态工厂方法(Static Factory Method)模式。
简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。
通常它根据自变量的不同返回不同类的实例。
2.简单工厂模式的实质是由一个工厂类根据传入参数,动态的决定应该创建出哪一个产品类的实例。
3.简单工厂模式的构成:1)工厂类(Creator)角色:担任这个角色的是简单工厂模式的核心,含有与应用紧密相关的商业逻辑。
工厂类在客户端直接调用下创建产品对象,它往往由一个具体类实现。
2)抽象产品(Product)角色:担任这个角色的类是简单工厂模式所创建的对象的父类,或他们共同拥有的接口。
抽象产品角色可以用一个接口或抽象类实现。
3)具体产品(Concrete Product)角色:简单工厂模式所创建的任何对象都是这个角色的实例,具体产品角色由一个具体类实现。
4.自己实现简单工厂模式1)抽象产品角色2)具体产品角色3)工厂类角色4)客户端调用5)上例中有一个接口类型的Person作为抽象产品角色,他的两个产品(Chinese和American)实现了这个接口,作为具体的产品角色。
在工厂类PersonFactory中,提供了一个静态的工厂方法getPerson返回一个Person类型。
该方法根据传过来的参数决定到底生成哪一个类的实例。
在客户端如果需要使用Chinese和American这两个类的实例的时候,可以使用工厂方法PersonFactory来获得,只需要传入一定的参数就能生成对应的想要的对象。
这样在客户端根本就看不到new两个具体的产品的代码了,降低了代码的耦合性。
5.典型应用:Spring框架的IOC(依赖注入机制)就是很好的使用了这个设计模式,使用户需要使用一个类的时候无需自己new出一个实例。
只需要配置好后,提供一个参数就能获得对应的实例。
大大降低了代码之间的耦合性。
三.模板方法模式1.模板方法模式定义一个操作中的算法骨架,而将一些步骤延伸到子类中去,使得子类可以不改变一个算法的结构,即可重新定义该算法的某些特定步骤。
这里需要复用的是算法的结构,也就是步骤,而步骤的实现可以在子类中完成。
2.也就是父类只是规定一些步骤(方法的顺序),而不关注每一个步骤到底是怎么实现的。
3.模板方法的使用场合:1)一次性实现一个算法的不变部分,并且将可变的行为留给子类来完成。
2)各子类的公共的行为应该被提取出来并集中到一个公共父类中以避免代码的重复。
首先识别现在代码的不同之处,并且把不同之处分离成为新的操作,最后用一个调用这些新的操作的模板方法来替代这些不同的代码。
3)控制子类的扩展4.模板方法模式的组成:1)父类角色:提供模板(可以用抽象类但是不能用接口,因为接口无法规定步骤)。
2)子类角色:为模板提供实现。
5.实现自己的模板方法模式父类角色如下(一般是一个抽象方法):子类角色如下以上两个角色就是模板方法模式的所有角色,使用如下:使用的时候直接调用父类规定好步骤的template方法就好。
6.对照上面的例子重新解释下最开始对模班方法的介绍:1)首先父类中的template方法规定了操作中算法的骨架(结构,步骤)。
2)定义的三个方法method1,method2,method3都是抽象的所以必然延伸到子类中去实现。
3)在子类中我们重写了三个抽象的方法,但是不会重写template方法,所以无法改变算法的结构(步骤),个人感觉为了防止template方法被改写可以在父类中定义的时候加上final关键字定义成终态方法(不能被重写的方法)。
4)Template方法(算法)的各个步骤需要到子类中去实现。
5)我们调用的时候是调用的子类继承下来的template方法,而不用去管具体的步骤。
7.在junit3中不管我们怎么写测试代码,所有测试方法都是按照先执行setUp(),再执行测试方法,再执行tearDown(),而这三个方法中的具体代码我们必须具体实现。
所以这里是典型用到了模板方法模式的地方。
当然在junit4中是使用注解但是核心还是一样,只不过是换成了注解@Before,@Test和@After。
8.其中JUnit3中实现了模板方法模式的那个template方法如下:四.策略模式JDK中涉及该模式的地方主要是TreeSet和TreeMap中需要传入一个指定的排序规则的地方1.策略模式(Strategy Pattern)中体现了两个非常基本的面向对象的设计的原则1)封装变化的概念2)编程中使用接口而不是对接口的实现2.策略模式的定义:1)定义了一组算法,将每个算法都封装起来,并且使他们之间可以互换。
2)策略模式使这些算法在客户端调用它们的时候能够互不影响的变化3.策略模式的组成1)抽象策略角色:策略类,通常由一个接口或者抽象类实现。
如JDK中的Comparator接口2)具体策略角色:包装了具体的算法和行为。
如我们自己实现的Comparator接口并实现compare(Object o1,Object o2)方法3)环境角色:持有一个策略类的引用,并最终给客户端调用的。
如JDK中的TreeSet,TreeMap,当我们要创建TreeSet对象时,我们一般需要传入一个Comparator接口类型的引用,用来接收自定义的实现类根据不同的实现类实现不同的排序方式。
4.策略模式的实现1)策略模式的用意是针对一组算法,将每一个算法封装到具有共同接口的独立类中,从而使他们可以相互替换。
2)策略模式使得算法可以在不影响客户端的情况下发生变化。
使用策略模式可以把行为和环境分割开来3)环境类负责维护和查询行为类,各种算法则在具体的策略中提供。
由于算法和环境独立开来,算法的修改都不会影响环境和客户端。
5.策略模式的编写步骤:1)对策略对象(具体的算法的类的对象)定义一个公共接口2)编写策略类(具体的算法),该类实现了上面的公共接口3)在使用策略对象的类中保存一个对策略对象(公共接口)的引用6.策略模式的缺点1)客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
2)造成很多策略类3)解决方法可以采用工厂方法。
7.实现自己的策略模式1)抽象的策略角色如下:2)具体的策略角色上面是两个具体的策略类,其中实现Comparator接口是为了让这个类是一个Comparator类型的,可以让自己实现的sort方法的Collections.sort()当成Comparator类型传到第二个参数中实现按照自己定义的compare 方法来排序。
与策略模式没有关系。
如果仅仅关注策略模式可以直接看实现的sort方法就可以了。
3)环境角色环境角色中持有一个对抽象策略类的一个引用,在自己定义的排序方法中调用了该引用的排序方法。
由于该引用是一个接口类型,那么到底调用的是哪个类的排序方法就看这个引用的实际类型了。
4)客户使用的代码和Person类(一些代码省略)客户端调用的时候,先new一个具体的角色,通过set方法或者是构造方法传入到环境角色的对象中然后直接调用环境角色的排序方法就可以了。
如果想使用不同的排序方法,只需要往环境变量中设置不同的具体策略角色对象就可以了。
五.代理模式1.代理模式的作用是:为其它对象提供一种代理以控制对这个对象的访问2.代理模式分为两种:静态代理和动态代理。
3.在某些情况下,一个客户不想或者不能直接引用另外一个对象,而代理模式可以在客户和目标之间起到中介的作用。
4.代理模式一般涉及到的角色有:1)抽象角色:声明真实对象和代理对象共同的接口2)代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口一边在任何时刻能代替真实对象同时,代理对象可以在执行真实对象操作时,附加其它的操作,相当与对真实对象进行封装。
3)真实角色:代理角色所代表的真实对象,使我们最终要引用的对象5.自己实现静态代理1)抽象角色如下2)真实角色如下:3)代理角色如下:4)客户端调用如下:6.实现自己的动态代理1)抽象角色如下2)真实角色如下3)动态代理角色如下3)客户端调用4)以上动态代码和静态代理的代码的区别主要在代理类和客户端调用两个地方。
动态代理类不需要实现那个抽象角色的接口了,在JDK中提供动态代理支持的接口是InvocationHandler。
实现该接口并且重写它的invoke方法。
5)以上例子从客户端的代码开始分析首先第一行通过动态代理类中定义的的factory方法获得一个RealSubject(真实对象)的代理对象(再来看看factory 这个方法,这个方法第一行获得了一个传入对象的Class对象在通过Proxy类的静态方法和传入对象的Class对象动态生成一个传入对象的代理对象。