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();
System.out.println(subject.getClass());
}
}
|
过程:
(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()
{
}
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)比较灵活。因为有钩子方法,因此,子类的实现也可以影响父类中主逻辑的运行。
缺点:每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象