Java中几种常用设计模式

Java中几种常用设计模式

Java 中一般认为有23种设计模式,当然暂时不需要所有的都会,但是其中常见的几种设计模式应该去掌握。
总体来说设计模式分为三大类:
1.创建型模式,共五种:单例模式工厂方法模式抽象工厂模式建造者模式、原型模式。
2.结构型模式,共七种:适配器模式装饰器模式代理模式、外观模式、桥接模式、组合模式、享元模式。
3.行为型模式,共十一种:策略模式观察者模式、模板方法模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

一. 单例模式

基本概念:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

1.饿汉式

public class Singleton{
    private static Singleton singleton = new Singleton();
    private Singleton(){
    }
    public static Singleton getInstance(){
        return singleton;
    }
}

调用

Singleton.getInstance().method();

2.懒汉式

public class Singleton {  
    /* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */  
    private static Singleton instance = null;  
  
    /* 私有构造方法,防止被实例化 */  
    private Singleton() {}  
  
    /* 1:懒汉式,静态工程方法,创建实例 */  
    public static Singleton getInstance() {  
        if (instance == null) {  
            instance = new Singleton();  
        }  
        return instance;  
    }  
	protected void method() {  
        System.out.println("SingletonInner");  
    }
}

调用

Singleton.getInstance().method();  

优点:延迟加载(需要的时候才去加载),适合单线程操作
缺点: 线程不安全,在多线程中很容易出现不同步的情况,如在数据库对象进行的频繁读写操作时。

3.双重线程检查模式

public class SingletonInner {  
    private static volatile SingletonInner sInst = null;  // 添加 volatile  
    /** 
     * 私有的构造函数 
     */  
    private SingletonInner() {}  
  
    public static SingletonInner getInstance() {  
        if (inst == null) {
            synchronized (SingletonInner.class) {if (inst == null) {
                    sInst = new SingletonInner();
                }
            }
        }
        return sInst;  
    }
    protected void method() {  
        System.out.println("SingletonInner");  
    }  
}

调用

Singleton.getInstance().method(); 

优点:线程安全,支持延时加载,调用效率高
缺点: 写法复杂,不简洁

4.内部类的实现

public class SingletonInner {  
    /** 
     * 内部类实现单例模式 
     * 延迟加载,减少内存开销   
     */  
    private static class SingletonHolder {  
        private static SingletonInner instance = new SingletonInner();  
    }  
  
    /** 
     * 私有的构造函数 
     */  
    private SingletonInner() {}  
  
    public static SingletonInner getInstance() {  
        return SingletonHolder.instance;  
    }  
  
    protected void method() {  
        System.out.println("SingletonInner");  
    }  
}

调用

Singleton.getInstance().method(); 

优点:延迟加载,线程安全(java中class加载时互斥的),也减少了内存消耗,推荐使用内部类方式。

二.工厂方法模式

基本概念:为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。

分为三类:

  • 简单工厂模式Simple Factory:不利于产生系列产品;
  • 工厂方法模式Factory Method:又称为多形性工厂;
  • 抽象工厂模式Abstract Factory:又称为工具箱,产生产品族,但不利于产生新的产品;

这三种模式从上到下逐步抽象,并且更具一般性。GOF在《设计模式》一书中将工厂模式分为两类:工厂方法模式(Factory Method)与抽象工厂模式(Abstract Factory)。将简单工厂模式(Simple Factory)看为工厂方法模式的一种特例,两者归为一类。

1.简单工厂模式

简单工厂模式又称静态工厂方法模式。重命名上就可以看出这个模式一定很简单。它存在的目的很简单:定义一个用于创建对象的接口。

在简单工厂模式中,一个工厂类处于对产品类实例化调用的中心位置上,它决定那一个产品类应当被实例化, 如同一个交通警察站在来往的车辆流中,决定放行那一个方向的车辆向那一个方向流动一样。

先来看看它的组成:

  • 工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑。在java中它往往由一个具体类实现。
  • 抽象产品角色:它一般是具体产品继承的父类或者实现的接口。在java中由接口或者抽象类来实现。
  • 具体产品角色:工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现。

示例代码:

public class Factory{ //getClass 产生Sample 一般可使用动态类装载装入类。
    public static Sample creator(int which){ 
        if (which==1)
            return new SampleA();
        else if (which==2)
            return new SampleB();
    }
}

还有一种目前比较流行的规范是把静态工厂方法命名为valueOf或者getInstance

valueOf:该方法返回的实例与它的参数具有同样的值,例如:

Integer a=Integer.valueOf(100); //返回取值为100的Integer对象

getInstance:返回的实例与参数匹配,例如:

Calendar cal=Calendar.getInstance(Locale.CHINA); //返回符合中国标准的日历

2.工厂方法模式

工厂方法模式是简单工厂模式的进一步抽象化和推广,工厂方法模式里不再只由一个工厂类决定那一个产品类应当被实例化,这个决定被交给抽象工厂的子类去做。
来看下它的组成:

  • 抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。
  • 具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象
  • 抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。
  • 具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。

工厂方法模式使用继承自抽象工厂角色的多个子类来代替简单工厂模式中的“上帝类”。正如上面所说,这样便分担了对象承受的压力;而且这样使得结构变得灵活 起来——当有新的产品(即暴发户的汽车)产生时,只要按照抽象产品角色、抽象工厂角色提供的合同来生成,那么就可以被客户使用,而不必去修改任何已有的代 码。可以看出工厂角色的结构也是符合开闭原则的!

示例代码:

//抽象产品
public interface TV {
    void play();
}
//具体产品
public class HaierTV implements TV {

    @Override
    public void play() {
        System.out.println("海尔TV播放中...");
    }
}
//具体产品
public class HisenseTV implements TV {

    @Override
    public void play() {
        System.out.println("创维TV播放中...");
    }
}
//抽象工厂
public interface TVFactory {
    TV produceTV();
}
//具体工厂
public class HaierTVFactory implements TVFactory {
    @Override
    public TV produceTV() {
        System.out.println("海尔电视机工厂生产海尔电视机");
        return new HaierTV();
    }
}
//具体工厂
public class HisenseTVFactory implements TVFactory {
    @Override
    public TV produceTV() {
        System.out.println("创维电视机工厂生产创维电视机");
        return new HisenseTV();
    }
}
//测试类
public class Client {
    public static void main(String[] args) {
        TVFactory factory=new HaierTVFactory();
        TV tv = factory.produceTV();
        tv.play();
    }
}

可以看出工厂方法的加入,使得对象的数量成倍增长。当产品种类非常多时,会出现大量的与之对应的工厂对象,这不是我们所希望的。因为如果不能避免这种情 况,可以考虑使用简单工厂模式与工厂方法模式相结合的方式来减少工厂类:即对于产品树上类似的种类(一般是树的叶子中互为兄弟的)使用简单工厂模式来实 现。

简单工厂和工厂方法模式的比较

工厂方法模式和简单工厂模式在定义上的不同是很明显的。工厂方法模式的核心是一个抽象工厂类,而不像简单工厂模式, 把核心放在一个实类上。工厂方法模式可以允许很多实的工厂类从抽象工厂类继承下来, 从而可以在实际上成为多个简单工厂模式的综合,从而推广了简单工厂模式。
反过来讲,简单工厂模式是由工厂方法模式退化而来。设想如果我们非常确定一个系统只需要一个实的工厂类, 那么就不妨把抽象工厂类合并到实的工厂类中去。而这样一来,我们就退化到简单工厂模式了。

三.抽象工厂模式

示例代码:

//抽象工厂(有多个产品)
public interface EFactory {
    TV produceTV();
    AC produceAC();
}
//具体工厂(有多个产品)
public class HaierFactory implements EFactory {
    @Override
    public TV produceTV() {
        return new HaierTV();
    }

    @Override
    public AC produceAC() {
        return new HaierAC();
    }
}
//具体工厂(有多个产品)
public class HisenseFactory implements EFactory {
    @Override
    public TV produceTV() {
        return new HisenseTV();
    }

    @Override
    public AC produceAC() {
        return new HisenseAC();
    }
}
//抽象产品
public interface TV {
    void play();
}
//抽象产品
public interface AC {
    void control();
}
//具体产品
public class HaierTV implements TV {
    @Override
    public void play() {
        System.out.println("海尔电视机播放中...");
    }
}
//具体产品
public class HisenseTV implements TV {
    @Override
    public void play() {
        System.out.println("创维电视机播放中...");
    }
}
//具体产品
public class HaierAC implements AC {
    @Override
    public void control() {
        System.out.println("海尔空调正在调温...");
    }
}
//具体产品
public class HisenseAC implements AC {
    @Override
    public void control() {
        System.out.println("创维空调正在调温...");
    }
}
//测试类
public class Client {
    public static void main(String[] args) {
        EFactory factory = new HisenseFactory();
        AC ac = factory.produceAC();
        ac.control();
    }
}

在抽象工厂模式中,抽象产品 (AbstractProduct) 可能是一个或多个,从而构成一个或多个产品族(Product Family)。 在只有一个产品族的情况下,抽象工厂模式实际上退化到工厂方法模式。

总结

  1. 简单工厂模式是由一个具体的类去创建其他类的实例,父类是相同的,父类是具体的。
  2. 工厂方法模式是有一个抽象的父类定义公共接口,子类负责生成具体的对象,这样做的目的是将类的实例化操作延迟到子类中完成。
  3. 抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无须指定他们具体的类。它针对的是有多个产品的等级结构。而工厂方法模式针对的是一个产品的等级结构。

四.建造者模式

基本概念:是一种对象构建的设计模式,它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。

Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们。用户不知道内部的具体构建细节。Builder模式是非常类似抽象工厂模式,细微的区别大概只有在反复使用中才能体会到。

UML结构图:

https://images2015.cnblogs.com/blog/331079/201606/331079-20160629172237218-374383367.gif

上图是Strategy 模式的结构图,让我们可以进行更方便的描述:

  • Builder:为创建一个Product对象的各个部件指定抽象接口。
  • ConcreteBuilder:实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,提供一个检索产品的接口
  • Director:构造一个使用Builder接口的对象。
  • Product:表示被构造的复杂对象。ConcreateBuilder创建该产品的内部表示并定义它的装配过程。

为何使用

是为了将构建复杂对象的过程和它的部件解耦。注意:是解耦过程和部件。
因为一个复杂的对象,不但有很多大量组成部分,如汽车,有很多部件:车轮、方向盘、发动机,还有各种小零件等等,部件很多,但远不止这些,如何将这些部件装配成一辆汽车,这个装配过程也很复杂(需要很好的组装技术),Builder模式就是为了将部件和组装过程分开。

如何使用

首先假设一个复杂对象是由多个部件组成的,Builder模式是把复杂对象的创建和部件的创建分别开来,分别用Builder类和Director类来表示。

首先,需要一个接口,它定义如何创建复杂对象的各个部件:

public interface Builder {
   //创建部件A  比如创建汽车车轮void buildPartA();
   //创建部件B 比如创建汽车方向盘void buildPartB();
   //创建部件C 比如创建汽车发动机void buildPartC();
   //返回最后组装成品结果 (返回最后装配好的汽车)
   //成品的组装过程不在这里进行,而是转移到下面的Director类中进行.
   //从而实现了解耦过程和部件
    Product getResult();
}

用Director构建最后的复杂对象,而在上面Builder接口中封装的是如何创建一个个部件(复杂对象是由这些部件组成的),也就是说Director的内容是如何将部件最后组装成成品:

public class Director {
    private Builder builder;
    public Director( Builder builder ) {
        this.builder = builder;
   }
   // 将部件partA partB partC最后组成复杂对象
   //这里是将车轮 方向盘和发动机组装成汽车的过程
    public void construct() {
        builder.buildPartA();
        builder.buildPartB();
        builder.buildPartC();
    }
}

Builder的具体实现ConcreteBuilder:

  • 通过具体完成接口Builder来构建或装配产品的部件;
  • 定义并明确它所要创建的是什么具体东西;
  • 提供一个可以重新获取产品的接口。
public class ConcreteBuilder implements Builder {
 Part partA, partB, partC;
 public void buildPartA() {
  //这里是具体如何构建
 }
 public void buildPartB() {
  //这里是具体如何构建
 }
 public void buildPartC() {
  //这里是具体如何构建
 }
 public Product getResult() {
  //返回最后组装成品结果
 }
}

复杂对象:产品Product:

public interface Product { }

复杂对象的部件:

public interface Part { }

我们看看如何调用Builder模式:

ConcreteBuilder builder = new ConcreteBuilder();
Director director = new Director( builder );
director.construct();
Product product = builder.getResult();

Builder模式的应用

在Java实际使用中,我们经常用到"池"(Pool)的概念,当资源提供者无法提供足够的资源,并且这些资源需要被很多用户反复共享时,就需要使用池。"池"实际是一段内存,当池中有一些复杂的资源的"断肢"(比如数据库的连接池,也许有时一个连接会中断),如果循环再利用这些"断肢",将提高内存使用效率,提高池的性能。修改Builder模式中Director类使之能诊断"断肢"断在哪个部件上,再修复这个部件。

五.适配器模式

基本概念:适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。

适配器模式的用途

用电器做例子,笔记本电脑的插头一般都是三相的,即除了阳极、阴极外,还有一个地极。而有些地方的电源插座却只有两极,没有地极。电源插座与笔记本电脑的电源插头不匹配使得笔记本电脑无法使用。这时候一个三相到两相的转换器(适配器)就能解决此问题,而这正像是本模式所做的事情。

适配器模式的结构
适配器模式有类的适配器模式对象的适配器模式两种不同的形式。

类适配器模式:

https://images2015.cnblogs.com/blog/331079/201606/331079-20160629172436390-555012550.png

在上图中可以看出,Adaptee类并没有sampleOperation2()方法,而客户端则期待这个方法。为使客户端能够使用Adaptee类,提供一个中间环节,即类Adapter,把Adaptee的API与Target类的API衔接起来。Adapter与Adaptee是继承关系,这决定了这个适配器模式是类的:

  • 目标(Target)角色:这就是所期待得到的接口。注意:由于这里讨论的是类适配器模式,因此目标不可以是类。
  • 源(Adapee)角色:现在需要适配的接口。
  • 适配器(Adaper)角色:适配器类是本模式的核心。适配器把源接口转换成目标接口。显然,这一角色不可以是接口,而必须是具体类。

对象适配器模式:

https://images2015.cnblogs.com/blog/331079/201606/331079-20160629172515640-753317336.png

从上图可以看出,Adaptee类并没有sampleOperation2()方法,而客户端则期待这个方法。为使客户端能够使用Adaptee类,需要提供一个包装(Wrapper)类Adapter。这个包装类包装了一个Adaptee的实例,从而此包装类能够把Adaptee的API与Target类的API衔接起来。Adapter与Adaptee是委派关系,这决定了适配器模式是对象的。

示例代码:

public interface Target {
    /**
     * 这是源类Adaptee也有的方法
     */
    public void sampleOperation1(); 
    /**
     * 这是源类Adapteee没有的方法
     */
    public void sampleOperation2(); 
}

public class Adaptee {
    public void sampleOperation1(){}
}

适配器类:

public class Adapter {
    private Adaptee adaptee;
    public Adapter(Adaptee adaptee){
        this.adaptee = adaptee;
    }
    /**
     * 源类Adaptee有方法sampleOperation1
     * 因此适配器类直接委派即可
     */
    public void sampleOperation1(){
        this.adaptee.sampleOperation1();
    }
    /**
     * 源类Adaptee没有方法sampleOperation2
     * 因此由适配器类需要补充此方法
     */
    public void sampleOperation2(){
        //写相关的代码
    }
}

类适配器和对象适配器的权衡

  • 类适配器使用对象继承的方式,是静态的定义方式;而对象适配器使用对象组合的方式,是动态组合的方式。
  • 对于类适配器由于适配器直接继承了Adaptee,使得适配器不能和Adaptee的子类一起工作,因为继承是静态的关系,当适配器继承了Adaptee后,就不可能再去处理 Adaptee的子类了。
  • 对于对象适配器一个适配器可以把多种不同的源适配到同一个目标。换言之,同一个适配器可以把源类和它的子类都适配到目标接口。因为对象适配器采用的是对象组合的关系,只要对象类型正确,是不是子类都无所谓。
  • 对于类适配器适配器可以重定义Adaptee的部分行为,相当于子类覆盖父类的部分实现方法。
  • 对于对象适配器要重定义Adaptee的行为比较困难,这种情况下,需要定义Adaptee的子类来实现重定义,然后让适配器组合子类。虽然重定义Adaptee的行为比较困难,但是想要增加一些新的行为则方便的很,而且新增加的行为可同时适用于所有的源。
  • 对于类适配器,仅仅引入了一个对象,并不需要额外的引用来间接得到Adaptee。
  • 对于对象适配器,需要额外的引用来间接得到Adaptee。

建议尽量使用对象适配器的实现方式,多用合成或聚合、少用继承。当然,具体问题具体分析,根据需要来选用实现方式,最适合的才是最好的。

适配器模式的优点

  • 更好的复用性:系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。
  • 更好的扩展性:在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。

适配器模式的缺点

  过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。

六.装饰器模式

基本概念:装饰模式(Decorator),动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。

UML结构图

https://images2015.cnblogs.com/blog/331079/201606/331079-20160629172628015-2044014507.png

上图是Decorator 模式的结构图,让我们可以进行更方便的描述:

  • Component是定义一个对象接口,可以给这些对象动态地添加职责。
  • ConcreteComponent是定义了一个具体的对象,也可以给这个对象添加一些职责。

Decorator是装饰抽象类,继承了Component,从外类来扩展Component类的功能,但对于Component来说,是无需知道Decorator存在的。ConcreteDecorator就是具体的装饰对象,起到给Component添加职责的功能。

如何使用

假设情景:某人装扮自己形象,穿衣服,裤子,鞋子,戴帽子等来把自己给包装起来,需要把所需的功能按正确的顺序串联起来进行控制,我们应该如何设计才能做到呢?如下,先看下代码结构图:

先创建一个接口类:Component.java

public interface Component {    
    void show();
}

创建一个具体的 ConcreteComponent 来实现 Component 接口:Person.java

public class Person implements Component{
    private String name;
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Person(String name){
        this.name = name;
    }

    @Override
    public void show() {
        System.out.println("装扮的" + name);
    }
}

创建装饰类 Decorator 实现 Component 接口

public class Decorator implements Component{
    private Component mComponent;
    public void decoratorObj(Component component){
        mComponent = component;
    }

    @Override
    public void show() {
        if(mComponent != null){
            mComponent.show();
        }
    }
}

分别创建具体的装饰类:Jeans.java , Pelisse.java, Sandal.java ...等等,分别继承 Decorator.java 类

/** 牛仔裤 */
public class Jeans extends Decorator {
    @Override
    public void show(){
        System.out.println("穿牛仔裤");
        super.show();
    }
    
}

客户端测试类

/**
 * 装饰模式测试客户端
 */
public class DecoratorClient {
    public static void main(String[] args) {
        System.out.println("Welcome to Andy.Chen Blog!" +"\n" 
                   +"Decorator Patterns." +"\n");
        
        Person mPerson = new Person("Andy");
        
        Sandal mSandal = new Sandal();
        Jeans mJeans = new Jeans();
        TShirt mShirt = new TShirt();
        
        mShirt.decoratorObj(mPerson);
        mJeans.decoratorObj(mShirt);
        mSandal.decoratorObj(mJeans);
        mSandal.show(); 
    }
}

测试结果

Welcome to Andy.Chen Blog!
Decorator Patterns.
  
穿凉鞋
穿牛仔裤
穿T-Shirt
装扮的Andy

总结

Decorator模式有以下的优缺点:

  • 比静态继承更灵活与对象的静态继承相比,Decorator模式提供了更加灵活的向对象添加职责的方式,可以使用添加和分离的方法,用装饰在运行时刻增加和删除职责。使用继承机制增加职责需要创建一个新的子类,如果需要为原来所有的子类都添加功能的话,每个子类都需要重写,增加系统的复杂度,此外可以为一个特定的Component类提供多个Decorator,这种混合匹配是适用继承很难做到的。
  • 避免在层次结构高层的类有太多的特征,Decorator模式提供了一种“即用即付”的方法来添加职责,他并不试图在一个复杂的可订制的类中支持所有可预见的特征,相反可以定义一个简单的类,并且用Decorator类给他逐渐的添加功能,从简单的部件组合出复杂的功能。
  • Decorator 与它的Component不一样Decorator是一个透明的包装,如果我们从对象标识的观点出发,一个被装饰了的组件与这个组件是有差别的,因此使用装饰时不应该以来对象标识。
  • 产生许多小对象,采用Decorator模式进行系统设计往往会产生许多看上去类似的小对象,这些对象仅仅在他们相互连接的方式上有所不同。

七.代理模式

  1. 代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理 对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的 功能操作,即扩展目标对象的功能。

  2. 被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象

  3. 代理模式有不同的形式, 主要有三种 静态代理、动态代理 (JDK代理、接口代 理)和 Cglib代理 (可以在内存动态的创建对象,而不需要实现接口, 他是属于 动态代理的范畴) 。

1.静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一 起实现相同的接口或者是继承相同父类 。

应用实例

  1. 定义一个接口:ITeacherDao

  2. 目标对象TeacherDAO实现接口ITeacherDAO

  3. 使用静态代理方式,就需要在代理对象TeacherDAOProxy中也实现ITeacherDAO

  4. 调用的时候通过调用代理对象的方法来调用目标对象.

  5. 特别提醒:代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来 调用目标对象的方法。

public interface ITeacherDao {
    void teacher();
}

public class TeacherDao implements ITeacherDao {
    @Override
    public void teacher() {
        System.out.println("老师授课中......");
    }
}

//代理类,静态代理
public class TeacherDaoProxy implements ITeacherDao {
    private ITeacherDao target;//目标对象,通过接口来聚合
    public TeacherDaoProxy(ITeacherDao target) {
        this.target = target;
    }
    @Override
    public void teacher() {
        System.out.println("代理开始 完成某些操作......");
        target.teacher();
        System.out.println("提交......");
    }
}

public class Client {
    public static void main(String[] args) {
        //创建目标对象(别代理对象)
        TeacherDao teacherDao = new TeacherDao();
        //创建代理对象,同时将目标对象传递给代理对象
        TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);
        //通过代理对象调用到被代理对象的方法
        teacherDaoProxy.teacher();
    }
}

静态代理优缺点

  1. 优点:在不修改目标对象的功能前提下, 能通过代理对象对目标功能扩展

  2. 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类

  3. 一旦接口增加方法,目标对象与代理对象都要维护

2.动态代理

  1. 代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理

  2. 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象

  3. 动态代理也叫做:JDK代理、接口代理

public interface ITeacherDao {
    void teacher();
}

public class TeacherDao implements ITeacherDao {
    @Override
    public void teacher() {
        System.out.println("老师授课中......");
    }
}

public class ProxyFactory {
    //维护一个目标对象
    private Object target;
    //对target进行初始化
    public ProxyFactory(Object target) {
        this.target = target;
    }
    //给目标对象生成一个代理对象
    public Object getProxyInstance(){
        /**
         * public static Object newProxyInstance(
         * ClassLoader loader,//使用handler对象的classloader对象来加载我们的代理对象
         * Class<?>[] interfaces,//这里为代理类提供的接口是真实对象实现的接口,这样代理对象就能像真实对象一样调用接口中的所有方法
         * InvocationHandler h//每一个动态代理类的调用处理程序都必须实现InvocationHandler接口,并且每个代理类的实例都关联到了实现该接口的动态代理类调用处理程序中,当我们通过动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现InvocationHandler接口类的invoke方法来调用
         * )
         */
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("before invoke~~");
                        Object returnVal = method.invoke(target, args);
                        System.out.println("after invoke~~");
                        return returnVal;
                    }
                });
    }
}

public class Client {
    public static void main(String[] args) {
        TeacherDao teacherDao = new TeacherDao();
        ProxyFactory proxyFactory = new ProxyFactory(teacherDao);
        ITeacherDao proxyInstance = (ITeacherDao)proxyFactory.getProxyInstance();
//        System.out.println("ITeacherDao="+proxyInstance);
        System.out.println("内存中的动态代理对象类型:"+proxyInstance.getClass());
        proxyInstance.teacher();
    }
}

3.Cglib代理

  1. 静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候目标对象只 是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现 代理-这就是Cglib代理。

  2. Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功 能扩展, 有些书也将Cglib代理归属到动态代理。

  3. Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接 口.它广泛的被许多AOP的框架使用,例如Spring AOP,实现方法拦截 。

  4. 在AOP编程中如何选择代理模式: 1. 目标对象需要实现接口,用JDK代理 2. 目标对象不需要实现接口,用Cglib代理。

  5. Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类。

public class TeacherDao {

	public String teach() {
		System.out.println(" 老师授课中  , 我是cglib代理,不需要实现接口 ");
		return "hello";
	}
}

public class ProxyFactory implements MethodInterceptor {

	//维护一个目标对象
	private Object target;
	
	//构造器,传入一个被代理的对象
	public ProxyFactory(Object target) {
		this.target = target;
	}

	//返回一个代理对象:  是 target 对象的代理对象
	public Object getProxyInstance() {
		//1. 创建一个工具类
		Enhancer enhancer = new Enhancer();
		//2. 设置父类
		enhancer.setSuperclass(target.getClass());
		//3. 设置回调函数
		enhancer.setCallback(this);
		//4. 创建子类对象,即代理对象
		return enhancer.create();
		
	}
	

	//重写  intercept 方法,会调用目标对象的方法
	@Override
	public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("Cglib代理模式 ~~ 开始");
		Object returnVal = method.invoke(target, args);
		System.out.println("Cglib代理模式 ~~ 提交");
		return returnVal;
	}

}

public class Client {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//创建目标对象
		TeacherDao target = new TeacherDao();
		//获取到代理对象,并且将目标对象传递给代理对象
		TeacherDao proxyInstance = (TeacherDao)new ProxyFactory(target).getProxyInstance();

		//执行代理对象的方法,触发intecept 方法,从而实现 对目标对象的调用
		String res = proxyInstance.teach();
		System.out.println("res=" + res);
	}

}

八.观察者模式

基本概念:观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。观察者模式又叫发布-订阅(Publish/Subscribe)模式。

UML结构图

https://images2015.cnblogs.com/blog/331079/201606/331079-20160629172359577-997239228.gif

上图是Observer 模式的结构图,让我们可以进行更方便的描述:

  • Subject类:它把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察着。抽象主题提供一个接口,可以增加和删除观察着对象。
  • Observer类:抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。
  • ConcreteSubject类:具体主题,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。
  • ConcreteObserver类:具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。

如何使用

天气预报项目需求,具体要求如下:

  1. 气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去(比如 发布到自己的网站或第三方)。

  2. 需要设计开放型API,便于其他第三方也能接入气象站获取数据。

  3. 提供温度、气压和湿度的接口

  4. 测量数据更新时,要能实时的通知给第三方

先创建一个Subject接口:

public interface Subject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}

创建Observer接口:

public interface Observer {
    void update(double temperature,double pressure,double humidity);
}

创建目标类:

public class WeatherData implements Subject{

    private double temperature;//气温
    private double pressure;//气压
    private double humidity;//湿度

    private ArrayList<Observer> observers;

    public WeatherData() {
        observers=new ArrayList<Observer>();
    }

    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }

    @Override
    public void removeObserver(Observer o) {
        observers.remove(o);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(temperature,pressure,humidity);
        }
    }
    public void setData(double temperature,double pressure,double humidity){
        this.temperature = temperature;
        this.pressure = pressure;
        this.humidity = humidity;
        notifyObservers();
    }
}

创建一个观察者类

public class BaiduSite implements Observer {

    private double temperature;//气温
    private double pressure;//气压
    private double humidity;//湿度

    @Override
    public void update(double temperature,double pressure,double humidity) {
        this.temperature = temperature;
        this.pressure = pressure;
        this.humidity = humidity;
        display();
    }
    public void display(){
        System.out.println("========百度站点========");
        System.out.println("***气温:"+temperature+" 气压:"+pressure+" 湿度:"+humidity+"***");
    }
}

创建另一个观察者类

public class SinaSite implements Observer {

    private double temperature;//气温
    private double pressure;//气压
    private double humidity;//湿度

    @Override
    public void update(double temperature,double pressure,double humidity) {
        this.temperature = temperature;
        this.pressure = pressure;
        this.humidity = humidity;
        display();
    }
    public void display(){
        System.out.println("========新浪站点========");
        System.out.println("气温:"+temperature+" 气压:"+pressure+" 湿度:"+humidity);
    }
}

客户端测试

public class Client {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        BaiduSite baiduSite = new BaiduSite();
        SinaSite sinaSite = new SinaSite();
        weatherData.registerObserver(baiduSite);
        weatherData.registerObserver(sinaSite);

        System.out.println("天气预报跟新,通知观察者");
        weatherData.setData(23.3,88.6,30.7);
        System.out.println("--------------------------------");
        System.out.println("百度站点取消天气预报服务");
        weatherData.removeObserver(baiduSite);//观察者取消订阅
        System.out.println("天气预报跟新,通知观察者");
        weatherData.setData(19.8,86.5,31.7);
    }
}

程序运行结果如下

天气预报跟新,通知观察者
========百度站点========
***气温:23.3 气压:88.6 湿度:30.7***
========新浪站点========
气温:23.3 气压:88.6 湿度:30.7
--------------------------------
百度站点取消天气预报服务
天气预报跟新,通知观察者
========新浪站点========
气温:19.8 气压:86.5 湿度:31.7
posted @ 2020-11-20 21:19  no1486  阅读(281)  评论(0编辑  收藏  举报