Java设计模式

设计模式是一个软件设计师必须掌握的技能,笔者在目前的项目开发过程中并没有过多的使用,深感惋惜,借此文章总结下Java中常用设计模式相关知识点,为今后的项目开发预先铺路。

设计模式可以说不是新的知识,是对开发流程等相关过程的简化,类似于Spring等框架,是为开发者省力的,系统越大作用越明显。

Java中的设计模式通常分为三类:

  1. 创建型,用于通过特定方式生成对象实例,例如单例模式、建造者模式、工厂模式、抽象工厂模式等;
  2. 结构型,用于将多个对象组织成更大的结构,例如代理模式、适配器模式、门面模式、桥接模式等;
  3. 行为型,用于帮助系统间各对象的通信或者控制复杂系统中的流程,例如观察者模式、命令模式、中介者模式等;

本文依次介绍如下设计模式:

  1. 单例模式;
  2. 工厂模式;
  3. 抽象工厂模式;
  4. 代理模式;
  5. 命令模式;
  6. 策略模式;
  7. 门面模式;
  8. 桥接模式;
  9. 观察者模式;
  10. 建造者模式;
  11. 拦截器模式;
  12. 责任链模式;


1.单例模式

意义:在开发过程中有些类创建多个实例是没有意义的,例如数据源的SessionFactory,相反创建多个实例还会加大系统的额外开销。

实现(程序模拟实现一个SessionFactory单例对象):

package com.lsm1998.test01;

/**
 * 作者:刘时明
 * 日期:2018/11/25
 * 时间:19:15
 * 说明:单例模式Demo
 */
public class MySessionFactory
{
    private static MySessionFactory sessionFactory;

    private MySessionFactory()
    {
    }

    public static MySessionFactory getInstance()
    {
        if (sessionFactory == null)
        {
            sessionFactory = new MySessionFactory();
        }
        return sessionFactory;
    }

    public static void main(String[] args)
    {
        MySessionFactory sessionFactory1 = MySessionFactory.getInstance();
        MySessionFactory sessionFactory2 = MySessionFactory.getInstance();
        // 返回true
        System.out.println(sessionFactory1==sessionFactory2);
    }
}

 

2.工厂模式

意义:笔者认为工厂模式的意义有两个,如下

  1. 增强创建对象的灵活性,相对于new关键字来说,每次创建一次不同的对象需要使用一次new,灵活性较强;
  2. 降低对象之间调用关系的耦合程度,例如A类调用B实例的方法,使用传统的new关键字需要在代码中使用new类名来获取对象,当后期出现一个C类来取代B类时则需要修改所有的B类,而使用工厂模式则可以绕过类名来创建对象;

实现(程序实现一个汽车工厂类,根据创建对象传入的参数创建对应的实例):

package com.lsm1998.test02;

/**
 * 作者:刘时明
 * 日期:2018/11/25
 * 时间:19:15
 * 说明:工厂模式 顶层接口
 */
public interface Car
{
    String getCarName();
}

********************分割线********************

package com.lsm1998.test02;

/**
 * 作者:刘时明
 * 日期:2018/11/25
 * 时间:19:23
 * 说明:工厂模式 底层实现类
 */
public class Benz implements Car
{
    @Override
    public String getCarName()
    {
        return "奔驰";
    }
}

********************分割线********************

package com.lsm1998.test02;

/**
 * 作者:刘时明
 * 日期:2018/11/25
 * 时间:19:22
 * 说明:工厂模式 底层实现类
 */
public class BWM implements Car
{
    @Override
    public String getCarName()
    {
        return "宝马";
    }
}

********************分割线********************

package com.lsm1998.test02;

/**
 * 作者:刘时明
 * 日期:2018/11/25
 * 时间:19:50
 * 说明:模拟拥有一辆车的人
 */
public class People
{
    private Car car;

    public People(Car car)
    {
        this.car = car;
    }

    public void showCar()
    {
        /**
         *  不使用工厂模式的代码
         *  拥有宝马车的情况
         *  car=new BWM();
         *  拥有奔驰车的情况
         *  car=new Benz();
         */
        System.out.println("我的车是:" + car.getCarName());
    }
}

********************分割线********************

package com.lsm1998.test02;

/**
 * 作者:刘时明
 * 日期:2018/11/25
 * 时间:19:24
 * 说明:汽车工厂
 */
public class CarFactory
{
    /**
     * 根据传入的汽车名称生产对应的汽车对象
     *
     * @param carName
     * @return
     */
    public Car getCar(String carName) throws Exception
    {
        if ("BWM".equalsIgnoreCase(carName))
        {
            return new BWM();
        } else if ("Benz".equalsIgnoreCase(carName))
        {
            return new Benz();
        } else
        {
            throw new Exception("没有名为:" + carName + "的汽车");
        }
    }

    public static void main(String[] args) throws Exception
    {
        CarFactory factory=new CarFactory();
        Car car1=factory.getCar("bwm");
        System.out.println(car1.getCarName());
        Car car2=factory.getCar("benz");
        System.out.println(car2.getCarName());

        People people1=new People(car1);
        people1.showCar();
        People people2=new People(car2);
        people2.showCar();

        // 模拟对象生产异常
        Car car3=factory.getCar("xxx");
        System.out.println(car3.getCarName());
    }
}

 

3.抽象工厂

意义:简单工厂模式直接生产对象,而抽象工厂模式不会直接生产对象,只是提供一个接口,具体的工厂再实现这个接口。

简而言之,工厂模式直接生产对象,而抽象工厂根据生产的目标对象先匹配对应的对象工厂,再由匹配的对象工厂生产目标对象。

实现(BWM和Benz类、Car接口与工厂模式相同,此处省略):

package com.lsm1998.test03;

/**
 * 作者:刘时明
 * 日期:2018/11/25
 * 时间:20:41
 * 说明:抽象工厂接口
 */
public interface AbstractFactory
{
    Car createCar(String carName);
}

************分割线************

package com.lsm1998.test03;

/**
 * 作者:刘时明
 * 日期:2018/11/25
 * 时间:20:46
 * 说明:负责生产奔驰的工厂
 */
public class BenzFactory implements AbstractFactory
{
    @Override
    public Car createCar(String carName)
    {
        return new Benz();
    }
}

************分割线************

package com.lsm1998.test03;

/**
 * 作者:刘时明
 * 日期:2018/11/25
 * 时间:20:45
 * 说明:负责生产宝马的工厂
 */
public class BWMFactory implements AbstractFactory
{
    @Override
    public Car createCar(String carName)
    {
        return new BWM();
    }
}

************分割线************

package com.lsm1998.test03;

/**
 * 作者:刘时明
 * 日期:2018/11/25
 * 时间:20:48
 * 说明:Base工厂,负责目标对象与实例工厂的匹配工作
 */
public class BaseFactory implements AbstractFactory
{
    @Override
    public Car createCar(String carName)
    {
        AbstractFactory factory = null;
        if ("BWM".equalsIgnoreCase(carName))
        {
            factory = new BWMFactory();
        } else if ("Benz".equalsIgnoreCase(carName))
        {
            factory = new BenzFactory();
        }
        if (factory != null)
        {
            return factory.createCar(carName);
        }
        return null;
    }
}

************分割线************

package com.lsm1998.test03;

/**
 * 作者:刘时明
 * 日期:2018/11/25
 * 时间:20:55
 * 说明:抽象工厂测试
 */
public class Test
{
    public static void main(String[] args)
    {
        BaseFactory factory=new BaseFactory();
        Car car1=factory.createCar("bwm");
        System.out.println(car1.getCarName());

        Car car2=factory.createCar("benz");
        System.out.println(car2.getCarName());
    }
}

 

4.代理模式

意义:笔者认为代理模式最大的作用在于提供一个中介者,例如创建一个系统开销很大、耗时较长的对象,客户端在发出创建改对象的请求后又迫切需要使用该对象,此时就可以额外创建一个代理对象先返回给客户端,避免了无谓的等待,代理模式还有一个很重要的使用是AOP代理,可以在执行一个方法前后插入一些通用的操作,例如JDBC事务。

代理模式的实现:JDK动态代理或者CGLIB动态代理都可以实现,二者的区别在于前者依赖于接口,不依赖第三方jar包,而后者不依赖于接口,但依赖于第三方jar包(笔者认为CGLIB动态代理更为强大、适用)

实现(程序通过JDK动态代理和CGLIB动态代理分别实现方法执行的事务切入):

package com.lsm1998.test04;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:13:29
 * 说明:模拟事务的类
 */
public class TxMath
{
    public void beginTx()
    {
        System.out.println("---开始事务---");
    }

    public void endTx()
    {
        System.out.println("---结束事务---");
    }
}

************分割线************

package com.lsm1998.test04;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:12:42
 * 说明:Math顶层接口
 */
public interface MyMath
{
    int add(int a, int b);

    int sub(int a, int b);

    void log();
}

************分割线************

package com.lsm1998.test04;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:13:29
 * 说明:MyMath接口实现类
 */
public class MyMathImpl implements MyMath
{
    @Override
    public int add(int a, int b)
    {
        return a + b;
    }

    @Override
    public int sub(int a, int b)
    {
        return a - b;
    }

    @Override
    public void log()
    {
        System.out.println("log日志方法执行");
    }
}


************分割线************

package com.lsm1998.test04;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:13:31
 * 说明:基于JDK内置功能的动态代理的处理类
 */
public class MyInvocationHandler implements InvocationHandler
{
    // 需要被代理的对象
    private Object object;

    public void setObj(Object object)
    {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
        TxMath txMath = new TxMath();
        txMath.beginTx();
        Object result = method.invoke(object, args);
        System.out.println("result=" + result);
        txMath.endTx();
        return result;
    }
}

************分割线************

package com.lsm1998.test04;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:15:54
 * 说明:不依赖于任何接口的Math类
 */
public class DifferentMath
{
    public int add(int a, int b)
    {
        return a + b;
    }
}

************分割线************

package com.lsm1998.test04;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:15:44
 * 说明:基于Cglib jar包的动态代理处理类
 */
public class CglibInvocationHandler implements MethodInterceptor
{
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable
    {
        TxMath math = new TxMath();
        math.beginTx();
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("result=" + result);
        math.endTx();
        return result;
    }
}

************分割线************

package com.lsm1998.test04;

import net.sf.cglib.proxy.Enhancer;

import java.lang.reflect.Proxy;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:15:17
 * 说明:动态代理对象工厂,传入一个实例,获取其对应的代理对象
 */
public class MyProxyFactory
{
    public static Object getProxy(Object target)
    {
        MyInvocationHandler handler = new MyInvocationHandler();
        // 设置代理对象
        handler.setObj(target);
        // 传入的参数分别是:当前类加载器,target类的父接口Class对象数组,JDK动态代理的处理类
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
    }

    public static Object getCglibtProxy(Object target)
    {
        CglibInvocationHandler handler = new CglibInvocationHandler();
        // 获取CGLIB enhancer增强类对象
        Enhancer enhancer = new Enhancer();
        // 设置代理对象类型
        enhancer.setSuperclass(target.getClass());
        // 定义代理逻辑对象为当前对象,要求当前对象实现MethodInterceptor方法
        enhancer.setCallback(handler);
        // 生成并返回代理对象
        return enhancer.create();
    }
}

************分割线************

package com.lsm1998.test04;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:14:12
 * 说明:测试类
 */
public class Test
{
    public static void main(String[] args)
    {
        try
        {
            // JDK动态代理
            MyMath proxy=new MyMathImpl();
            MyMath math=(MyMath)MyProxyFactory.getProxy(proxy);
            System.out.println(math.add(2,4));
            math.log();
            // CGLIB动态代理
            DifferentMath proxy2=new DifferentMath();
            DifferentMath differentMath=(DifferentMath)MyProxyFactory.getCglibtProxy(proxy2);
            System.out.println(differentMath.add(2,4));
        }catch (Exception e)
        {
            System.err.println("生成代理对象出现错误");
        }
    }
}

 

5.命令模式

意义:可以把一次处理行为作为参数传递给一个方法,一次处理行为就可以看作一个命令,命令模式最直观的好处在于它的灵活性和解耦性,只需要修改少量的命令就可以替换系统中全部应用该命令的操作。

笔者看来,命令模式的实现就是运用匿名内部类,将一次命令封装到一个接口中,再到处理方法将该接口作为参数传入,处理方法只需执行命令接口方法即可。

实现(程序实现一个通过命令模式自由指定处理行为的数组处理类):

package com.lsm1998.test05;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:16:36
 * 说明:命令接口
 */
public interface Command<E>
{
    E resultMethod(int[] target);
}

************分割线************

package com.lsm1998.test05;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:16:35
 * 说明:需要使用命令处理的类
 */
public class TargetArray
{
    public <E> E arrMethod(int[] target, Command<E> cmd)
    {
        // 直接调用命令接口的方法返回
        return cmd.resultMethod(target);
    }
}

************分割线************

package com.lsm1998.test05;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:16:37
 * 说明:测试类
 */
public class Test
{
    public static void main(String[] args)
    {
        TargetArray pa = new TargetArray();
        int[] target = {5, 8, 9, 2, 4, 7};
        // 通过命令模式完成求和操作
        int result1 = pa.arrMethod(target, t ->
        {
            int sum = 0;
            for (int temp : t)
            {
                sum += temp;
            }
            return sum;
        });

        // 通过命令模式完成求积操作
        int result2 = pa.arrMethod(target, t ->
        {
            int product = 1;
            for (int temp : t)
            {
                product *= temp;
            }
            return product;
        });

        System.out.println("result1=" + result1);
        System.out.println("result2=" + result2);
    }
}

 

6.策略模式

意义:策略模式用于封装系列的算法,客户端程序可以自由匹配一种算法,达到算法的自由切换。

实现(模拟一个商品折扣策略,对于不同的用户会有不同的折扣策略,使用策略模式可以灵活切换折扣算法):

package com.lsm1998.test06;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:20:48
 * 说明:折扣策略接口
 */
public interface DiscountStrategy
{
    double getDisCount(double originPrice);
}

************分割线************

package com.lsm1998.test06;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:20:50
 * 说明:默认策略类
 */
public class DefaultDiscount implements DiscountStrategy
{
    @Override
    public double getDisCount(double originPrice)
    {
        return originPrice * 0.7;
    }
}

************分割线************

package com.lsm1998.test06;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:20:50
 * 说明:VIP策略类
 */
public class VipDiscount implements DiscountStrategy
{
    @Override
    public double getDisCount(double originPrice)
    {
        return originPrice * 0.5;
    }
}

************分割线************

package com.lsm1998.test06;

/**
 * 作者:刘时明
 * 日期:2018/11/26 0026
 * 时间:20:51
 * 说明:折扣策略的匹配类
 */
public class DiscountContext
{
    private DiscountStrategy strategy;

    /**
     * 定制的折扣策略
     * @param strategy
     * @return
     */
    public DiscountContext(DiscountStrategy strategy)
    {
        this.strategy = strategy;
    }

    /**
     * 默认的折扣策略
     */
    public DiscountContext()
    {
        this.strategy = new DefaultDiscount();
    }

    public double getDiscountPrice(double price)
    {
        return this.strategy.getDisCount(price);
    }

    public void chageDiscount(DiscountStrategy strategy)
    {
        this.strategy = strategy;
    }
}

************分割线************

package com.lsm1998.test06;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:20:55
 * 说明:测试类
 */
public class Test
{
    public static void main(String[] args)
    {
        int num=100;
        // 使用默认的折扣策略
        DiscountContext context=new DiscountContext();
        System.out.println(context.getDiscountPrice(num));
        // 使用指定的VIP折扣策略
        DiscountContext context2=new DiscountContext(new VipDiscount());
        System.out.println(context2.getDiscountPrice(num));
        // 切换到默认的折扣策略
        context2.chageDiscount(new DefaultDiscount());
        System.out.println(context2.getDiscountPrice(num));
    }
}



 

7.门面模式

意义:门面模式可以为一些类提供一个简化的接口,从而简化访问这些类的复杂性,只需要调用门面类的业务方法就可以实现以往一连串的业务调用。

实现(程序模拟实现一个点餐的业务逻辑,需要有支付、烹饪、送餐三个阶段):

package com.lsm1998.test07;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:21:50
 * 说明:支付接口
 */
public interface Payment
{
    String pay();
}

************分割线************

package com.lsm1998.test07;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:21:54
 * 说明:支付接口实现类
 */
public class PaymentImpl implements Payment
{
    @Override
    public String pay()
    {
        String food="快餐";
        System.out.println("你已经向收银员支付了费用,你购买的食物是:"+food);
        return food;
    }
}

************分割线************

package com.lsm1998.test07;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:21:50
 * 说明:烹饪接口
 */
public interface Cook
{
    String cook(String food);
}

************分割线************

package com.lsm1998.test07;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:21:57
 * 说明:烹饪接口实现类
 */
public class CookImpl implements Cook
{
    @Override
    public String cook(String food)
    {
        System.out.println("厨师正在烹饪:"+food);
        return food;
    }
}

************分割线************

package com.lsm1998.test07;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:22:00
 * 说明:服务员送餐接口
 */
public interface Waiter
{
    void service(String food);
}

************分割线************

package com.lsm1998.test07;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:22:01
 * 说明:服务员送餐接口实现类
 */
public class WaiterImpl implements Waiter
{
    @Override
    public void service(String food)
    {
        System.out.println("服务员已经将:" + food + "端过来了,请慢用");
    }
}

************分割线************

package com.lsm1998.test07;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:22:10
 * 说明:提供点餐服务的门面类
 */
public class Facade
{
    // 封装提供业务逻辑的三个类
    private Payment payment;
    private Cook cook;
    private Waiter waiter;

    public Facade()
    {
        this.payment = new PaymentImpl();
        this.cook =  new CookImpl();
        this.waiter = new WaiterImpl();
    }

    /**
     * 封装点餐业务方法
     */
    public void serveFood()
    {
        String food=payment.pay();
        cook.cook(food);
        waiter.service(food);
    }
}


************分割线************

package com.lsm1998.test07;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:22:05
 * 说明:测试类
 */
public class Test
{
    public static void main(String[] args)
    {
        // 创建三个对象
        Payment payment=new PaymentImpl();
        Cook cook=new CookImpl();
        Waiter waiter=new WaiterImpl();
        // 依次调用三个方法实现一次点餐业务
        String food=payment.pay();
        cook.cook(food);
        waiter.service(food);

        // 使用门面模式
        Facade facade=new Facade();
        facade.serveFood();
    }
}




 

8.桥接模式

意义:是一种结构型模式,可以将类的多维度变化分离,降低类的多维度变化带来的额外开销,减少类的设计中的臃肿程度。

例如现实生活中的面馆菜单,有牛肉面、猪肉面,顾客可以根据口味选择辣味和不辣,那么就有四个对应的实例对象,分别是辣味牛肉面、不辣牛肉面、辣味猪肉面、不辣猪肉面,此时使用桥接模式可以分离辣味风格和面的品种。

实现:

package com.lsm1998.test08;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:23:48
 * 说明:辣味风格接口
 */
public interface Peppery
{
    String style();
}

************分割线************

package com.lsm1998.test08;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:23:49
 * 说明:加辣风格
 */
public class PepperyStyle implements Peppery
{
    @Override
    public String style()
    {
        return "辣味很重,很过瘾";
    }
}

************分割线************

package com.lsm1998.test08;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:23:50
 * 说明:不加辣风格
 */
public class PlainStyle implements Peppery
{
    @Override
    public String style()
    {
        return "味道清淡,有点痿,吃不了辣";
    }
}

************分割线************

package com.lsm1998.test08;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:23:52
 * 说明:辣味风格+面条品种的组合抽象类
 */
public abstract class AbstractNodele
{
    protected Peppery style;

    public AbstractNodele(Peppery style)
    {
        this.style=style;
    }

    public abstract void eat();
}

************分割线************

package com.lsm1998.test08;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:23:55
 * 说明:牛肉面对象与辣味风格的组合类
 */
public class BeefNoodle extends AbstractNodele
{
    public BeefNoodle(Peppery style)
    {
        super(style);
    }

    @Override
    public void eat()
    {
        System.out.println("牛肉面条,辣味风格:" + super.style.style());
    }
}

************分割线************

package com.lsm1998.test08;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:23:54
 * 说明:猪肉面对象与辣味风格的组合类
 */
public class ProkyNoodle extends AbstractNodele
{
    public ProkyNoodle(Peppery style)
    {
        super(style);
    }

    @Override
    public void eat()
    {
        System.out.println("猪肉面条,辣味风格:" + super.style.style());
    }
}

************分割线************

package com.lsm1998.test08;

/**
 * 作者:刘时明
 * 日期:2018/11/26
 * 时间:23:56
 * 说明:测试类
 */
public class Test
{
    public static void main(String[] args)
    {
        // 辣味牛肉面
        AbstractNodele nodele1=new BeefNoodle(new PepperyStyle());
        nodele1.eat();
        // 不辣牛肉面
        AbstractNodele nodele2=new BeefNoodle(new PlainStyle());
        nodele2.eat();
        // 辣味猪肉面
        AbstractNodele nodele3=new BeefNoodle(new PepperyStyle());
        nodele3.eat();
        // 不辣猪肉面
        AbstractNodele nodele4=new BeefNoodle(new PlainStyle());
        nodele4.eat();
    }
}




9.观察者模式

意义:当对象之间存在一对多依赖关系时,使用观察者模式可以让一个或者多个对象观察一个主题对象,当主题对象的状态发生变化时,系统能通知所有依赖于此对象的观察者对象,从而达到自动更新的目的,典型实现是发布/订阅的JMS机制。

例如我有一部手机打算发布到二手交易市场低价出售,而交易市场是多种多样的,此时我每到一个交易市场发布出售信息都需要自己跑一趟去告知,当手机在某个市场完成交易后有需要去其他市场删除出售信息,而使用观察者模式则可以让多个交易市场观察我这台二手手机对象,我只需要发布一次出售信息或者出售完毕信息,观察的交易市场会将我的交易信息自动更新上去。

实现:

package com.lsm1998.test09;

/**
 * 作者:刘时明
 * 日期:2018/11/27
 * 时间:10:58
 * 说明:观察者接口
 */
public interface MyObserver
{
    void update(Observable o,Object arg);
}

************分割线************

package com.lsm1998.test09;

/**
 * 作者:刘时明
 * 日期:2018/11/27
 * 时间:11:07
 * 说明:二手交易市场 1
 */
public class Market1 implements MyObserver
{
    @Override
    public void update(Observable o, Object arg)
    {
        System.out.println("这里是交易市场1,刚刚有位买家发布了一个信息:" + arg);
    }
}

************分割线************

package com.lsm1998.test09;

/**
 * 作者:刘时明
 * 日期:2018/11/27
 * 时间:11:07
 * 说明:二手交易市场 2
 */
public class Market2 implements MyObserver
{
    @Override
    public void update(Observable o, Object arg)
    {
        System.out.println("这里是交易市场2,刚刚有位先生发布了一个交易信息:" + arg);
    }
}

************分割线************

package com.lsm1998.test09;

/**
 * 作者:刘时明
 * 日期:2018/11/27
 * 时间:11:08
 * 说明:二手交易市场 3
 */
public class Market3 implements MyObserver
{
    @Override
    public void update(Observable o, Object arg)
    {
        System.out.println("这里是交易市场3,刚刚有位帅哥发布了一个信息:" + arg);
    }
}

************分割线************

package com.lsm1998.test09;

import java.util.ArrayList;
import java.util.List;

/**
 * 作者:刘时明
 * 日期:2018/11/27
 * 时间:11:02
 * 说明:被观察者的抽象基类
 */
public abstract class Observable
{
    // 存储对应的观察者
    List<MyObserver> observerList = new ArrayList<>();

    // 注册一个观察者
    public void registObserver(MyObserver o)
    {
        observerList.add(o);
    }

    // 删除一个观察者
    public void removeObserver(MyObserver o)
    {
        observerList.remove(o);
    }

    // 被观察者对其对应观察者的一次更新通知
    public void notifyObservers(Object value)
    {
        for (MyObserver o : observerList)
        {
            o.update(this, value);
        }
    }
}

************分割线************

package com.lsm1998.test09;

/**
 * 作者:刘时明
 * 日期:2018/11/27
 * 时间:11:09
 * 说明:准备出售的二手手机
 */
public class MyPhone extends Observable
{
    public void release()
    {
        notifyObservers("发布了出售信息,欢迎大家购买");
    }

    public void remove()
    {
        notifyObservers("手机已经卖出了,谢谢大家支持");
    }
}

************分割线************

package com.lsm1998.test09;

/**
 * 作者:刘时明
 * 日期:2018/11/27
 * 时间:11:18
 * 说明:测试类
 */
public class Test
{
    public static void main(String[] args)
    {
        // 创建我的二手手机对象,它是被观察者
        MyPhone phone=new MyPhone();

        // 创建所有交易市场对象,他们是观察者
        Market1 m1=new Market1();
        Market2 m2=new Market2();
        Market3 m3=new Market3();

        // 注册观察者
        phone.registObserver(m1);
        phone.registObserver(m2);
        phone.registObserver(m3);

        // 发布出售信息
        phone.release();

        // 假设此时手机已经在市场2被抢购了,此时需要在市场1、3发布信息
        System.out.println("--------------------");
        phone.removeObserver(m2);
        phone.remove();
        phone.registObserver(m2);
    }
}



 

10.建造者模式

意义:建造者模式的作用是用来分步创建复杂的对象,典型的应用是Mybatis的SqlSessionFactory对象的生成,有基础的读者可以发现SqlSessionFactoryBuilder类的build方法参数列表略显多样,既可以传入代表配置文件的Configuration对象,也可以传入输入流等等,Mybatis框架就是为其专门创建一个构造器SqlSessionFactoryBuilder,将SqlSessionFactory的构建过程和表示分开,隐藏了复杂对象的生成过程,降低了开发复杂度。

实现(程序模拟实现一个门票对象通过建造者模式的生成):

package com.lsm1998.test10;

import java.util.ArrayList;
import java.util.List;

/**
 * 作者:刘时明
 * 日期:2018/10/22
 * 时间:23:29
 * 说明:门票对象
 */
public class Ticket
{
    // 门票类型
    private List<String> types;
    // 门票价格
    private double price;

    public Ticket()
    {
        types=new ArrayList<>();
    }

    public double getPrice()
    {
        return price;
    }

    public void setPrice(double price)
    {
        this.price = price;
    }

    public List<String> getTypes()
    {
        return types;
    }

    public void setTypes(List<String> types)
    {
        this.types = types;
    }

    @Override
    public String toString()
    {
        return "Ticket{" +
                "types=" + types +
                ", price=" + price +
                '}';
    }
}

*************分割线*************

package com.lsm1998.test10;

/**
 * 作者:刘时明
 * 日期:2018/10/22
 * 时间:23:20
 * 说明:门票构建帮助类
 */
public class TicketHelper
{
    private Ticket ticket;

    public TicketHelper()
    {
        this(100.0);
    }

    public TicketHelper(double price)
    {
        this.ticket = new Ticket();
        this.ticket.setPrice(price);
    }

    public void buildAdult(boolean adult)
    {
        if (adult)
        {
            this.ticket.getTypes().add("成人");
        } else
        {
            this.ticket.getTypes().add("儿童");
            this.ticket.setPrice(ticket.getPrice() * 0.5);
        }
    }

    public void buildForSeat(boolean forSeat)
    {
        if (forSeat)
        {
            this.ticket.getTypes().add("有座");
        } else
        {
            this.ticket.getTypes().add("无座");
            this.ticket.setPrice(ticket.getPrice() * 0.75);
        }
    }

    public void buildSoldier(boolean soldier)
    {
        if (soldier)
        {
            this.ticket.getTypes().add("军人及其家属");
            this.ticket.setPrice(ticket.getPrice() * 0.75);
        } else
        {
            this.ticket.getTypes().add("非军人及其家属");
        }
    }

    public Ticket getTicket()
    {
        return ticket;
    }
}

*************分割线*************

package com.lsm1998.test10;

/**
 * 作者:刘时明
 * 日期:2018/10/22
 * 时间:23:29
 * 说明:门票构建对象
 */
public class TicketBuilder
{
    public static Ticket builder(TicketHelper helper)
    {
        return helper.getTicket();
    }
}

*************分割线*************

package com.lsm1998.test10;

/**
 * 作者:刘时明
 * 日期:2018/10/22
 * 时间:23:22
 * 说明:测试类
 */
public class Test
{
    public static void main(String[] args)
    {
        // 创建门票构建帮助类
        TicketHelper helper=new TicketHelper();
        // 设置门票对象属性,可以封装成一个配置类
        helper.buildAdult(true);
        helper.buildSoldier(true);
        helper.buildForSeat(true);
        // 通过构建对象获取实例
        Ticket ticket=TicketBuilder.builder(helper);
        System.out.println(ticket);
    }
}

 

11.拦截器模式

意义:拦截器模式与代理模式是十分类似的,二者的实现过程也是基本一致,都是通过JDK动态代理(当然基于Cglib的动态代理除外),相对不同的是,责任链模式需要绑定一个拦截器接口,该接口提供拦截过程中的逻辑处理方法,拦截器模式的作用不仅限于此,可以通过链式绑定拦截器实现一个责任链,详情请看‘12责任链模式’。

实现(程序模拟实现一个事务切入):

package com.lsm1998.test11;

/**
 * 作者:刘时明
 * 日期:2018/11/27
 * 时间:23:44
 * 说明:模拟Dao接口
 */
public interface Dao
{
    void save(Object o);

    void delete(Long id);
}

************分割线************

package com.lsm1998.test11;

/**
 * 作者:刘时明
 * 日期:2018/11/27
 * 时间:23:45
 * 说明:Dao接口实现类
 */
public class DaoImpl implements Dao
{
    @Override
    public void save(Object o)
    {
        System.out.println("保存一个对象:" + o);
    }

    @Override
    public void delete(Long id)
    {
        System.out.println("删除一个对象,id=" + id);
    }
}

************分割线************

package com.lsm1998.test11;

import java.lang.reflect.Method;

/**
 * 作者:刘时明
 * 日期:2018/11/27
 * 时间:23:37
 * 说明:自定义拦截器接口
 */
public interface Interceptor
{
    // 前置处理方法
    void before(Object proxy, Object target, Method method, Object[] args);

    // 后置处理方法
    void after(Object proxy, Object target, Method method, Object[] args);
}

************分割线************

package com.lsm1998.test11;

import java.lang.reflect.Method;

/**
 * 作者:刘时明
 * 日期:2018/11/28
 * 时间:0:05
 * 说明:自定义拦截器实现类
 */
public class MyInterceptor implements Interceptor
{
    @Override
    public void before(Object proxy, Object target, Method method, Object[] args)
    {
        System.out.println("开启事务");
    }

    @Override
    public void after(Object proxy, Object target, Method method, Object[] args)
    {
        System.out.println("提交事务");
    }
}

************分割线************

package com.lsm1998.test11;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 作者:刘时明
 * 日期:2018/11/27
 * 时间:23:39
 * 说明:通过JDK动态代理实现拦截功能
 */
public class InterceptorJdkProxy implements InvocationHandler
{
    // 真实对象
    private Object target;
    // 拦截器全限定名(包名+类名)
    private String interceptorClass;

    public InterceptorJdkProxy(Object target, String interceptorClass)
    {
        this.target = target;
        this.interceptorClass = interceptorClass;
    }

    /**
     * 绑定委托对象并返回一个【代理占位】
     *
     * @param target           真实对象名
     * @param interceptorClass 拦截器全限定名
     * @return 代理对象【代理占位】
     */
    public static Object bind(Object target, String interceptorClass)
    {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), new InterceptorJdkProxy(target, interceptorClass));
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
        if (interceptorClass == null)
        {
            // 没有设置拦截器则直接反射原有方法
            return method.invoke(target, args);
        }
        Object result;
        // 通过反射生成拦截器
        Interceptor interceptor = (Interceptor) Class.forName(interceptorClass).getConstructor().newInstance();
        // 设置前置方法
        interceptor.before(proxy, target, method, args);
        // 反射原有对象方法
        result = method.invoke(target, args);
        // 调用后置方法
        interceptor.after(proxy, target, method, args);
        return result;
    }
}

************分割线************

package com.lsm1998.test11;

/**
 * 作者:刘时明
 * 日期:2018/11/27
 * 时间:23:43
 * 说明:测试
 */
public class Test
{
    public static void main(String[] args)
    {
        Dao proxy=(Dao) InterceptorJdkProxy.bind(new DaoImpl(),"com.lsm1998.test11.MyInterceptor");
        // 模拟保存持久化一个对象
        proxy.save(new Object());
        // 模拟删除持久化一个对象
        proxy.delete(1l);
    }
}



 

责任链模式

意义:责任链模式就是在拦截器模式中多个拦截器链式绑定,类似于A拦截,而B拦截C,C拦截D,此时D对象就会被C、B、A三个对象拦截,由此,责任链模式拥有比代理模式更加强大的前置、后置处理功能,责任链模式比较典型的应用是Struts框架,Struts中配置功能、传参数、设置参数等大部分功能都是通过一系列拦截器完成的。

实现(Interceptor接口和InterceptorJdkProxy类沿用拦截器模式中的程序):

package com.lsm1998.test12;

import com.lsm1998.test11.Interceptor;

import java.lang.reflect.Method;

/**
 * 作者:刘时明
 * 日期:2018/11/27
 * 时间:23:54
 * 说明:拦截器1
 */
public class Interceptor1 implements Interceptor
{
    @Override
    public void before(Object proxy, Object target, Method method, Object[] args)
    {
        System.out.println("拦截器1的前置方法");
    }

    @Override
    public void after(Object proxy, Object target, Method method, Object[] args)
    {
        System.out.println("拦截器1的后置方法");
    }
}

************分割线************

package com.lsm1998.test12;

import com.lsm1998.test11.Interceptor;

import java.lang.reflect.Method;

/**
 * 作者:刘时明
 * 日期:2018/11/28
 * 时间:0:08
 * 说明:拦截器2
 */
public class Interceptor2 implements Interceptor
{
    @Override
    public void before(Object proxy, Object target, Method method, Object[] args)
    {
        System.out.println("拦截器2的前置方法");
    }

    @Override
    public void after(Object proxy, Object target, Method method, Object[] args)
    {
        System.out.println("拦截器2的后置方法");
    }
}

************分割线************

package com.lsm1998.test12;

import com.lsm1998.test11.Interceptor;

import java.lang.reflect.Method;

/**
 * 作者:刘时明
 * 日期:2018/11/27
 * 时间:23:54
 * 说明:拦截器3
 */
public class Interceptor3 implements Interceptor
{
    @Override
    public void before(Object proxy, Object target, Method method, Object[] args)
    {
        System.out.println("拦截器3的前置方法");
    }

    @Override
    public void after(Object proxy, Object target, Method method, Object[] args)
    {
        System.out.println("拦截器3的后置方法");
    }
}

************分割线************

package com.lsm1998.test12;

import com.lsm1998.test11.Dao;
import com.lsm1998.test11.DaoImpl;
import com.lsm1998.test11.InterceptorJdkProxy;

/**
 * 作者:刘时明
 * 日期:2018/11/28
 * 时间:0:08
 * 说明:测试类
 */
public class Test
{
    public static void main(String[] args)
    {
        Dao proxy1 = (Dao) InterceptorJdkProxy.bind(new DaoImpl(), "com.lsm1998.test12.Interceptor1");
        Dao proxy2 = (Dao) InterceptorJdkProxy.bind(proxy1, "com.lsm1998.test12.Interceptor2");
        Dao proxy3 = (Dao) InterceptorJdkProxy.bind(proxy2, "com.lsm1998.test12.Interceptor3");
        proxy3.save(new String("一个实体类"));
    }
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值