1.策略模式

1)定义:针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。

2)结构

这个模式涉及到三个角色:

  ●  环境(Context)角色:持有一个Strategy的引用。

  ●  抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。

  ●  具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。

3)算法实现

环境角色类:

 折叠原码
public class Context {
    //持有一个具体策略的对象
    private Strategy strategy;
    /**
     * 构造函数,传入一个具体策略对象
     * @param strategy    具体策略对象
     */
    public Context(Strategy strategy){
        this.strategy = strategy;
    }
    /**
     * 策略方法
     */
    public void contextInterface(){
         
        strategy.strategyInterface();
    }
     
}

抽象策略类:

 折叠原码
public interface Strategy {
    /**
     * 策略方法
     */
    public void strategyInterface();
}

具体策略类:

 折叠原码
public class ConcreteStrategyA implements Strategy {
 
    @Override
    public void strategyInterface() {
        //相关的业务
    }
 
}
 折叠原码
public class ConcreteStrategyB implements Strategy {
 
    @Override
    public void strategyInterface() {
        //相关的业务
    }
 
}
 折叠原码
public class ConcreteStrategyC implements Strategy {
 
    @Override
    public void strategyInterface() {
        //相关的业务
    }
 
}

4)使用范例

假设现在要设计一个贩卖各类书籍的电子商务网站的购物车系统。一个最简单的情况就是把所有货品的单价乘上数量,但是实际情况肯定比这要复杂。比如,本网站可能对所有的高级会员提供每本20%的促销折扣;对中级会员提供每本10%的促销折扣;对初级会员没有折扣。

  根据描述,折扣是根据以下的几个算法中的一个进行的:

  算法一:对初级会员没有折扣。

  算法二:对中级会员提供10%的促销折扣。

  算法三:对高级会员提供20%的促销折扣。

抽象折扣类:

 折叠原码
public interface MemberStrategy {
    /**
     * 计算图书的价格
     * @param booksPrice    图书的原价
     * @return    计算出打折后的价格
     */
    public double calcPrice(double booksPrice);
}

初级会员折扣类

 折叠原码
public class PrimaryMemberStrategy implements MemberStrategy {
 
    @Override
    public double calcPrice(double booksPrice) {
         
        System.out.println("对于初级会员的没有折扣");
        return booksPrice;
    }
 
}

中级会员折扣类

 折叠原码
public class IntermediateMemberStrategy implements MemberStrategy {
 
    @Override
    public double calcPrice(double booksPrice) {
 
        System.out.println("对于中级会员的折扣为10%");
        return booksPrice * 0.9;
    }
 
}

高级会员折扣类

 折叠原码
public class AdvancedMemberStrategy implements MemberStrategy {
 
    @Override
    public double calcPrice(double booksPrice) {
         
        System.out.println("对于高级会员的折扣为20%");
        return booksPrice * 0.8;
    }
}

价格类:

 

 折叠原码
public class Price {
    //持有一个具体的策略对象
    private MemberStrategy strategy;
    /**
     * 构造函数,传入一个具体的策略对象
     * @param strategy    具体的策略对象
     */
    public Price(MemberStrategy strategy){
        this.strategy = strategy;
    }
     
    /**
     * 计算图书的价格
     * @param booksPrice    图书的原价
     * @return    计算出打折后的价格
     */
    public double quote(double booksPrice){
        return this.strategy.calcPrice(booksPrice);
    }
}

客户端

 折叠原码
public class Client {
 
    public static void main(String[] args) {
        //选择并创建需要使用的策略对象
        MemberStrategy strategy = new AdvancedMemberStrategy();
        //创建环境
        Price price = new Price(strategy);
        //计算价格
        double quote = price.quote(300);
        System.out.println("图书的最终价格为:" + quote);
    }
 
}

5)优缺点:

    优点:Strategy类层次为Context定义了一系列的可供重用的算法或行为;提供了可以替换继承关系的办法;消除了一些if else条件语句

    缺点:客户端必须知道所有的策略类,并自行决定使用哪一个策略类;策略模式将造成产生很多策略类

2.代理模式

1)定义:为另一个对象提供一个替身或占位符以控制对这个对象的访问。

2)结构:

角色:

        Subject:为RealSubject和Proxy提供了接口。通过实现同一接口,Proxy在RealSubject出现的地方取代它。

        RealSubject:真正做事的对象,它是被proxy代理和控制访问的对象。

        Proxy:持有RealSubject的引用。某些情况下,Proxy还会负责RealSubject对象的创建与销毁

3)使用范例:

静态代理:

 折叠原码
/**
 * 真实对象和代理对象的共同接口
 *
 */
public abstract class Subject
{
    public abstract void request();
}
 折叠原码
/**
 * 真实角色
 */
public class RealSubject extends Subject
{
    @Override
    public void request()
    {
        System.out.println("From Real Subject!");
    }
}
 折叠原码
/**
 * 代理角色
 *
 */
public class ProxySubject extends Subject
{
    //代理角色对象内部含有对真实对象的引用
    private RealSubject realSubject;
 
    @Override
    public void request()
    {
        //在真实角色操作之前所附加的操作
        preRequest();
        if(null == realSubject)
        {
            realSubject = new RealSubject();
        }
         
        //真实角色所完成的事情
        realSubject.request();
        //在真实角色操作之后所附加的操作
        postRequest();
    }
     
    private void preRequest()
    {
        System.out.println("Pre Request.");
    }
    private void postRequest()
    {
        System.out.println("Post Request");
    }
}
 折叠原码
/**
 * 客户类
 
 */
public class Client
{
    public static void main(String[] args)
    {
        Subject subject = new ProxySubject();
        subject.request();       
    }   
}

 

动态代理:程序动态产生代理类

 折叠原码
public interface Subject
{
    public void request();
}
 折叠原码
public class RealSubject implements Subject
{
    @Override
    public void request()
    {
        System.out.println("From real subject!");
    }
}
 折叠原码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
 
/**
 * 该代理类的内部属性是Object类型,实际使用的时候通过该类的构造方法传递进来一个对象。
 * 该类实现了invoke()方法,该方法中的method.invoke()其实就是调用被代理对象的将要执行的方法,
 * 方法参数sub表示该方法从属于sub。
 * 通过动态代理类,我们可以在执行真实对象的方法前后加入自己的一些额外方法
 *
 */
public class DynamicSubject implements InvocationHandler
{
 
    //对真实对象的引用
    private Object sub;
     
    public DynamicSubject(Object obj)
    {
        this.sub = obj;
         
    }
     
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable
    {
        System.out.println("Before calling: " + method);
         
        //通过反射来调用方法
        method.invoke(sub, args);
         
        System.out.println("After calling: " + method);
        return null;
    }
 
}
 折叠原码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
 
public class Client
{
    public static void main(String[] args)
    {
        RealSubject realSubject = new RealSubject();
 
        InvocationHandler handler = new DynamicSubject(realSubject);
 
        Class<?> classType = handler.getClass();
 
        // 生成代理
        // 动态生成一个类(实现了指定的接口),生成类的对象,转换成接口类型
        System.out.println("classType:" + classType.getClassLoader());
        System.out.println("realSubject:" + realSubject.getClass().getClassLoader());
        Subject subject = (Subject) Proxy.newProxyInstance(classType
                .getClassLoader(), realSubject.getClass().getInterfaces(),
        handler);
 
        subject.request();
        // 调用方法时,转移给handler接管,由其中的invoke()方法实际完成方法执行
 
        System.out.println(subject.getClass());// 打印出:class $Proxy0
        // $Proxy0是在运行期间动态生成的一个类
 
 
    }
 
}

  过程:

       (1)代理对象方法被调用;

      (2)proxy会接着调用InvocationHandler的invoke()方法;

      (3)handler决定如何处理这个请求   

4)优缺点:

 优点:对外部提供统一的接口方法,而代理类在接口中实现对真实类的附加操作行为,从而可以在不影响外部调用情况下,进行系统扩展

 缺点:由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

3.模板方法模式

1)定义:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。

2)结构:

该模式包含两个角色:

(1) AbstractClass(抽象类):在抽象类中定义了一系列基本操作(PrimitiveOperations),这些基本操作可以是具体的,也可以是抽象的,每一个基本操作对应算法的一个步骤,在其子类中可以重定义或实现这些步骤。同时,在抽象类中实现了一个模板方法(Template Method),用于定义一个算法的框架,模板方法不仅可以调用在抽象类中实现的基本方法,也可以调用在抽象类的子类中实现的基本方法,还可以调用其他对象中的方法。

(2) ConcreteClass(具体子类):它是抽象类的子类,用于实现在父类中声明的抽象基本操作以完成子类特定算法的步骤,也可以覆盖在父类中已经实现的具体基本操作。

3)算法实现:

抽象类:

 折叠原码
abstract public class AbstractClass
{
    public void TemplateMethod()
    {
         
        primitiveMethod1();
         
        primitiveMethod2();
         
        doOperation3();
         
    }
     
    protected abstract void primitiveMethod1();
     
    protected abstract void primitiveMethod2();
     
    private final void doOperation3()
    {
         
        // do something
         
    }
  
    void hook() {} 
}

抽象类中包含有三个基本方法:

基本方法:

1. Abstract Method:由子类具体实现,完成具体的算法步骤。

2. Concrete Method:抽象类实现的final方法,子类不能override。

3. Hook Method:提供缺省的实现,子类可以在必要时进行扩展,钩子简化了子类的实现,它可以让子类能够有机会对模板方法中某些即将发生的(或刚刚发生的)步骤做出反应; 也可以作为控制条件,使得子类可以影响到抽象类中的算法流程

具体子类

 折叠原码
public class ConcreteClass extends AbstractClass
{
    public void primitiveMethod1()
    {
         
        System.out.println("primitiveMethod1();");
         
    }
     
    public void primitiveMethod2()
    {
         
        System.out.println("primitiveMethod2();");
         
    }
}

 

4)使用范例:

 折叠原码
package com.kaishengit.beverage; 
   
public abstract class Beverage { 
    /**
     * 冲泡咖啡或茶...流程
     */ 
    public final void create(){ 
        boilWater();//把水煮沸 
        brew();//用沸水冲泡... 
        pourInCup();//把...倒进杯子 
        addCoundiments();//加... 
           
        hook();//挂钩 
    
    //空实现方法 
    public void hook(){} 
   
    public abstract void addCoundiments(); 
   
    public abstract void brew(); 
       
    public void boilWater() { 
        System.out.println("煮开水"); 
    
       
    public void pourInCup() { 
        System.out.println("倒进杯子"); 
    

假如我们搞活动,喝一杯咖啡送一杯,修改咖啡(Coffee)类

咖啡类 折叠原码
package com.kaishengit.beverage;
 
public class Coffee extends Beverage{
 
    @Override
    public void addCoundiments() {
        System.out.println("添加糖和牛奶");   }
 
    @Override
    public void brew() {
        System.out.println("用水冲咖啡");
    }
     
    /**
     * 挂钩
     */
    @Override
    public void hook() {
        System.out.println("再来一杯");
    }
 
}

 

5)优缺点:

     优点:

      (1)容易扩展。一般来说,抽象类中的模版方法是不易反生改变的部分,而抽象方法是容易反生变化的部分,因此通过增加实现类一般可以很容易实现功能的扩展,符合开闭原则。

      (2)便于维护。对于模版方法模式来说,正是由于他们的主要逻辑相同,才使用了模版方法,假如不使用模版方法,任由这些相同的代码散乱的分布在不同的类中,维护起来是非常不方便的。

      (3)比较灵活。因为有钩子方法,因此,子类的实现也可以影响父类中主逻辑的运行。

     缺点:每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象