Java常用设计模式详解及其优缺点

本文将介绍:
简单工厂模式、工厂方法模式、抽象工厂模式、静态代理模式、JDK动态代理模式、cglib动态代理模式、策略模式、模板模式、装饰器模式、观察者模式

简单工厂模式

简单工厂模式又称为静态工厂方法模式,涉及到三个角色:
      1.工厂类角色:工厂方法模式的核心,工厂类在客户端的直接调用下创建产品对象;
      2.抽象产品角色:由工厂方法模式所创建的对象的父类,或者它们共有的接口;
      3.具体产品角色。
优点:该模式的核心是工厂类,该类含有必要的判断逻辑,什么时候创建哪一种类的实例,客户端可以免除直接创建产品对象的责任(不用直接在客户端new对象),而仅仅负责使用这个对象,简单工厂模式通过这样的做法实现了对责任的分割。
缺点:扩展困难,由于工厂类含有必要的判断逻辑创建哪个对象,所以违反了“开-闭 原则”。
代码:
工厂类:

public class ShapeFactory {
    public static Shape getShape(String shapeType) {
        if (shapeType == null) {
            return null;
        }
        if ("Circle".equals(shapeType)) {
            return new Circle();
        } else if ("Rectangle".equals(shapeType)) {
            return new Rectangle();
        } else if ("Square".equals(shapeType)) {
            return new Square();
        }
        return null;
    }
}

抽象产品类:

public interface Shape {
    void draw();
}

具体子类:

public class Circle implements Shape{

    @Override
    public void draw() {
        System.out.println("圆形");
    }
}

public class Rectangle implements Shape{

    @Override
    public void draw() {
        System.out.println("长方形");
    }

}

public class Square implements Shape{

    @Override
    public void draw() {
        System.out.println("正方形");
    }
}

客户端使用:

public class Main {
    public static void main(String[] args) {
        Shape circle = ShapeFactory.getShape("Circle");
        circle.draw();
    }
}

如果需要新增一个新的产品,就需要修改ShapeFactory这个工厂类,影响代码稳定性,对整个系统而言,工厂模式把具体使用new操作符的细节包装盒隐藏起来,在客户端就不必使用new来创建具体角色,下面给出类图:
Java常用设计模式详解及其优缺点_第1张图片

 

工厂方法模式

工厂方法模式涉及到的角色:
      1.抽象工厂角色:(并不是有抽象工厂,就是抽象工厂模式)它与应用程序是无关的,创建工厂的类必须实现这个接口或者继承这个抽象Java类;
      2.具体工厂角色:具体工厂角色含有与应用密切相关的逻辑,并受到应用程序的调用以创建产品对象;
      3.抽象产品角色:产品对象的共同父类或者共同的接口;
      4.具体产品角色:实现了抽象产品角色接口,工厂方法模式所创建的每一个对象都是某个具体产品角色的实例。
优点:符合 “开-闭 原则”,加入新产品是,无需修改其他具体工厂和具体产品,只需要添加一个具体工厂和具体产品即可。
缺点:一定程度上增加了系统的复杂度。
代码:
抽象工厂:(返回的一个抽象产品)

public interface Provider {
    Shape produce();
}

具体工厂:(实现抽象工厂,及其方法返回一个具体产品对象)

/**
 * 具体工厂-CircleFactory
 */
public class CircleFactory implements Provider {

    @Override
    public Shape produce() {
        return new Circle();
    }
}

/**
 * 具体工厂-RectangleFactory
 */
public class RectangleFactory implements Provider {
    @Override
    public Shape produce() {
        return new Rectangle();
    }
}

/**
 * 具体工厂-SquareFactory
 */
public class SquareFactory implements Provider {
    @Override
    public Shape produce() {
        return new Square();
    }
}

抽象产品:

public interface Shape {
    void draw();
}

具体产品类:(实现抽闲产品接口及其方法)

public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("圆形");
    }
}

public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("矩形");
    }
}

public class Square implements Shape {
    @Override
    public void draw() {
        System.out.println("正方形");
    }
}

客户端的使用:

public class Main {
    public static void main(String[] args) {
        Provider circleProvider = new CircleFactory();
        Shape circle = circleProvider.produce();
        circle.draw();
        Provider squareProvider = new SquareFactory();
        Shape square = squareProvider.produce();
        square.draw();
    }
}

produce()方法返回的是一个抽象类型Shape,而不是具体的哪一个产品,而客户端也不必知道所得到的产品的真是类型,这种多态性设计将工厂类选择创建哪一个产品,如何创建这个对象的细节完全封装在具体工厂类的内部。
如果想新增一种产品,只需要新增一个具体产品类和对应的具体工厂类即可,不需要修改其他代码。下面给出类图:Java常用设计模式详解及其优缺点_第2张图片
当抽象工厂只有一种的时候,再把工厂类创建的方法设置为静态方法,就演变成了简单工厂模式。工厂方法模式的别名:多态性工厂模式,是因为具体工厂类都有共同的接口或者共同的抽象父类。

 

抽象工厂模式

抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态,抽象工厂模式可以向客户端提供一个接口,使得客户端在不必指定产品的具体类型的情况下,创建多个产品族中的产品对象。
抽象工厂模式面对的问题是多个产品等级结构的系统设计。如何理解这句话?首先理解产品族的概念,所谓产品族,是指位于不同产品等级结构中,功能相关联的产品组成的家族。比如海尔工厂生产:海尔冰箱、海尔空调;格力工厂生产:格力空调,格力冰箱。如果使用工厂方法模式,那么生产冰箱需要一个工厂,生产空调需要一个工厂,每个产品等级都需要一个相应的工厂,随着产品等级结构的增加,工厂也随之增加,显然抽象工厂模式更有效率。
Java常用设计模式详解及其优缺点_第3张图片
图片来自https://www.cnblogs.com/Bobby0322/p/4181931.html
抽象工厂模式设计的角色:
      1.抽象工厂角色:声明生产一系列(一个产品族)抽象产品;
      2.具体工厂角色:执行生产一些列抽象产品的方法,生产一系列具体的产品;
      3.抽象产品角色
      4.具体产品角色
什么情况下考虑使用抽象工厂模式呢?
      1.一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的;
      2.这个系统的产品有多于一个的产品族,而系统只消费其中一某一族的产品;
      3.同属于同一个产品族的产品是在一起使用的;
      4.系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。

下面给出代码实例:
抽象产品:

/**
 * 抽象产品-空调
 */
public interface IAirconditioner {
    /**
     * 制冷
     */
    void cryogen();
}

/**
 * 抽象产品-冰箱
 */
public interface IRefrigerator {
    /**
     * 保存食品
     */
    void preserveFood();
}

具体产品:

public class HaierRefrigerator implements IRefrigerator {
    @Override
    public void preserveFood() {
        System.out.println("海尔冰箱保存食品!");
    }
}

public class GreeRefrigerator implements IRefrigerator {
    @Override
    public void preserveFood() {
        System.out.println("格力冰箱保存食品!");
    }
}

public class HaierAirConditioner implements IAirconditioner {
    @Override
    public void cryogen() {
        System.out.println("海尔空调制冷!");
    }
}

public class GreeAirConditioner implements IAirconditioner {
    @Override
    public void cryogen() {
        System.out.println("格力空调制冷!");
    }
}

抽象工厂:

/**
 * 抽象工厂
 */
public interface AbstractFactory {

    /**
     * 生产空调
     *
     * @return
     */
    IAirconditioner createAirconditoner();

    /**
     * 生产冰箱
     *
     * @return
     */
    IRefrigerator createRefrigerator();
}

具体工厂:

public class GreeFactory implements AbstractFactory {
    @Override
    public IAirconditioner createAirconditoner() {
        return new GreeAirConditioner();
    }
    @Override
    public IRefrigerator createRefrigerator() {
        return new GreeRefrigerator();
    }
}

public class HaierFactory implements AbstractFactory{
    @Override
    public IAirconditioner createAirconditoner() {
        return new HaierAirConditioner();
    }
    @Override
    public IRefrigerator createRefrigerator() {
        return new HaierRefrigerator();
    }
}

客户端的使用:

public class Main {
    public static void main(String[] args) {
        AbstractFactory greeFactory = new GreeFactory();
        AbstractFactory haierFactory = new HaierFactory();

        IAirconditioner greeAirConditioner = greeFactory.createAirconditoner();
        IRefrigerator greeRefrigerator = greeFactory.createRefrigerator();

        IAirconditioner haierAirConditioner = haierFactory.createAirconditoner();
        IRefrigerator haierRefrigerator = haierFactory.createRefrigerator();

        System.out.println("格力产品;");
        greeAirConditioner.cryogen();
        greeRefrigerator.preserveFood();

        System.out.println("海尔产品:");
        haierAirConditioner.cryogen();
        haierRefrigerator.preserveFood();

    }
}

优点:新增一个产品族很方便,只需要创建一个该产品族的工厂和具体产品即可,不需要改变抽象工厂和抽象产品接口;
缺点:抽象工厂模式的扩展性不如工厂方法模式,如果需要新增一个产品族里的一个产品,就需要修改多个接口,并修改已有的工厂类。(新增产品族容易,新增产品等级困难)
下面给出类图:Java常用设计模式详解及其优缺点_第4张图片

 

策略模式

策略模式属于对象的行为模式,其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类里,从而使得它们可以相互替换。
策略模式涉及到的角色:
      1.环境角色:持有一个策略类的引用;
      2.抽象策略角色:抽象角色,通常由一个接口或抽象类实现,此角色给出所有的具体策略类所需的接口;
      3.具体策略角色:包装了相关的算法或行为;
什么情况下考虑使用策略模式?
      1.如果在一个系统里有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为;
      2.一个系统需要动态地在几种算法中选择一种;
      3.一个系统的算法使用的数据不可以让客户端知道;
      4.如果一个对象有很多的行为,如果不用恰当的设计模式,这些行为就只能使用多重的if-else来实现,此时,使用策略模式,把这些行为转移到相应的具体策略类里面,就可以避免使用难以维护的if-else语句,并体现面向对象设计的概念。
下面给出代码实例
抽象策略角色:

public interface Strategy {
    void method();
}

具体策略角色:

public class ConcreteStrategy1 implements Strategy {

    @Override
    public void method() {
        System.out.println("strategy1");
    }

}

public class ConcreteStrategy2 implements Strategy {

    @Override
    public void method() {
        System.out.println("strategy2");
    }

}

环境角色:

public class Context {

    private Strategy strategy;

    /**
     * 构造函数,传入一个具体策略对象
     */
    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    /**
     * 策略方法
     */
    public void method() {
        this.strategy.method();
    }

}

客户端的使用:

public class Main {
    public static void main(String[] args) {
        Strategy strategy = new ConcreteStrategy1();
        Context context = new Context(strategy);
        context.method();
    }
}

策略模式需要客户端知道所有的策略类,并自行决定使用哪一个策略类,这是策略模式的缺点所在。
下面给出类图:
Java常用设计模式详解及其优缺点_第5张图片

装饰器模式

装饰器模式涉及到的角色:

  1. 抽象构件角色:给出一个抽象接口,以规范准备接收附加功能的对象;
  2. 具体构件角色:定义一个将要接收附加功能的类;
  3. 装饰器角色:持有一个构件对象的实例,并定义一个与抽象构件接口一致的接口;
  4. 具体装饰角色:负责给构件对象新增功能。

抽象构件角色:

public interface Logger {
    void log();

    void logTest();
}

具体构件角色:

public class ConcreteLogger implements Logger {

    @Override
    public void log() {
        System.out.println("ConcreteLogger");
    }

    @Override
    public void logTest() {
        System.out.println("logger test!");
    }
}

装饰器角色:

public class Decorator implements Logger {
    protected Logger logger;

    Decorator(Logger logger) {
        this.logger = logger;
    }

    @Override
    public void log() {
        if (logger != null) {
            logger.log();
        }
    }

    @Override
    public void logTest() {
        if (logger != null) {
            logger.logTest();
        }
    }
}

具体装饰角色:

public class Function1 extends Decorator {
    Function1(Logger logger) {
        super(logger);
    }

    @Override
    public void log() {
        super.log();
        method();
    }

    public void method() {
        System.out.println("method1");
    }
}

public class Function2 extends Decorator {

    Function2(Logger logger) {
        super(logger);
    }

    @Override
    public void log() {
        super.log();
        method();
    }

    @Override
    public void logTest() {
        super.log();
        methodTest();
    }

    public void method() {
        System.out.println("method2");
    }

    public void methodTest() {
        System.out.println("methodTest");
    }
}

客户端使用:

public class Main {
    public static void main(String[] args) {
        Logger concreteLogger = new ConcreteLogger();
        //原有的log功能
        concreteLogger.log();
        System.out.println("********");
        Decorator decorator = new Function1(concreteLogger);
        //装饰Function1以后的log功能
        decorator.log();
        System.out.println("********");
        Decorator decorator1 = new Function2(concreteLogger);
        //装饰Function2以后的log功能
        decorator1.log();
        decorator1.logTest();
    }
}

装饰器模式要求被装饰的类(具体构件角色)需要实现一个接口(抽象构件角色),装饰器负责增强这个接口里的方法,如果这个接口新增的方法,后续的改动会比较多,需要修改被装饰的类,装饰器类,被装饰的类(具体构件角色)和装饰器类都需要实现这个接口(抽象构件角色);装饰器角色不仅要实现抽象构件角色,还需要拥有这个抽象构件角色的引用。具体装饰角色继承这个装饰器类,并丰富父类的方法,以起到装饰的功能。

装饰器模式应当在什么情况下使用:

  1. 需要扩展一个类的功能,或给一个类增加附加责任;
  2. 需要动态地给一个对象增加功能,这些功能可以再动态地撤销;
  3. 需要增加由一些基本功能地排列组合而产生地非常大量的功能,从而使继承关系变得不现实;

代理模式

JDK动态代理

代理对象是由jvm生成的,不像静态代理,需要自己new一个代理对象出来,生成的代理对象也实现了目标对象实现的接口,因为这个代理对象是jdk相关代码生成的,所以这个称为jdk动态代理。

拦截器:
 

public class Interceptor implements InvocationHandler {

    private Object target;

    public Interceptor(Object target) {
        this.target = target;
    }

    /**
     * 可以在该方法里加强被代理类的功能
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("切面编程start");
        for (Object object : args){
            System.out.println("args:" +object);
        }
        method.invoke(this.target, args);
        System.out.println("切面编程end");
        return null;
    }

}

目标对象接口:

public interface ITarget {
    void business(String str);

    void method(String str,int i);
}

目标对象:

public class Target implements ITarget {

    @Override
    public void business(String str) {
        System.out.println("target "+str);
    }

    @Override
    public void method(String str, int i) {
        System.out.println("str = "+str);
        System.out.println("i = "+i);
    }

}

客户端使用:

public class Main {
    public static void main(String[] args) {
        /**目标对象*/
        Target target = new Target();
        /**拦截器*/
        Interceptor interceptor = new Interceptor(target);
        /**
         * 调用系统方法生成代理对象
         */
        ITarget iTarget = (ITarget) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                interceptor
        );

        iTarget.business("business");
        iTarget.method("method",1);
    }
}

cglib动态代理

相比jdk的模式,cglib少了一个接口类,因为cglib返回的代理对象是目标对象的子类,而jdk产生的代理对象和目标对象都实现同一个接口,在运行时期,cglib会生成被代理类的子类,并重写被代理对象的所有方法,从而作为代理对象,所以:代理的类不能为final,否则报错;目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法。

拦截器:

public class Interceptor implements MethodInterceptor {

    private Object target;

    public Interceptor(Object target){
        this.target = target;
    }

    /**
     * 返回代理对象
     * @return
     */
    public Object createProxy() {
        Enhancer enhancer = new Enhancer();
        //回调函数  拦截器
        enhancer.setCallback(this);
        //设置代理对象的父类,可以看到代理对象是目标对象的子类。所以这个接口类就可以省略了。
        enhancer.setSuperclass(this.target.getClass());
        return enhancer.create();
    }

    /**
     * 目标方法
     *
     * @param o
     * @param method
     * @param objects
     * @param methodProxy
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) 
            throws Throwable {
        System.out.println("执行方法:"+method.getName());
        System.out.println("cglib 动态代理 start");
        method.invoke(this.target,objects);
        System.out.println("cglib 动态代理 end");
        return null;
    }
}

目标类:

public class Target {

    public void business() {
        System.out.println("target business!");
    }

    public void test() {
        System.out.println("target test!");
    }

}

客户端使用:

public class Main {
    public static void main(String[] args) {
        
        Target target = new Target();
        Interceptor interceptor = new Interceptor(target);
        Target proxyObj = (Target) interceptor.createProxy();

        proxyObj.business();
        System.out.println("***********************");
        proxyObj.test();

    }
}

模板模式

模板模式涉及到的角色:

  1. 抽象模板角色
  2. 具体模板角色

定义抽象类并且声明一些抽象基本方法供子类实现不同逻辑,同时在抽象类中定义具体方法把抽象基本方法封装起来,这就是模板方法模式。

抽象模板角色:

public abstract class AbstractClassTemplate {

    public void template() {
        concreteMethod();
        if(doGetFlag()){
            abstractMethod1();
        }
        abstractMethod2();
    }
    /**
     * 基本方法一般用final修饰,防止子类修改,这个方法是所有继承这个模板的类共有的
     */
    public final void concreteMethod() {
        System.out.println("具体方法");
    }

    /**
     * 模板抽象方法,由子类具体实现
     */
    public abstract void abstractMethod1();

    /**
     * 模板抽象方法,由子类具体实现
     */
    public abstract void abstractMethod2();

    /**
     * 钩子方法
     * 一般do开头,如doGet() doPost()
     * @return
     */
    protected boolean doGetFlag() {
        return true;
    }

}

具体模板角色:

public class ConcreteClass extends AbstractClassTemplate {

    @Override
    public void abstractMethod1() {
        System.out.println("子类ConcreteClass abstractMethod1方法");
    }
    @Override
    public void abstractMethod2() {
        System.out.println("子类ConcreteClass abstractMethod2方法");
    }
    /**
     * 不同的子类实现不同的钩子方法
     * 以此来控制template方法的执行
     * @return
     */
    @Override
    public boolean doGetFlag(){
        //do something
        return false;
    }
}

客户端使用:

public class Main {
    public static void main(String[] args) {
        AbstractClassTemplate concreteClass = new ConcreteClass();
        concreteClass.template();
    }
}

未完待续。。。。。。

你可能感兴趣的:(随笔记录)