常用设计模式(二)

Strategy策略模式
将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换,由使用者决定使用哪种策略;通过策略类的等级结构来管理算法族,避免使用将算法的选择与算法本身的实现混合在一起if…else等多重判断;缺点是用户需要知道所有的算法实现,且由于每个算法都封装成类,因此产生的对象会很多;策略模式重点是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性;
组成:
环境类(Context):持有一个Strategy抽象策略类或策略接口的引用;
抽象策略类(Strategy):一般是一个接口或抽象类,定义所有支持的算法的公共接口;
具体策略类(ConcreteStrategy):封装了相关的算法或行为;

//抽象策略类
public interface UserStrategy {
	public double calPrice(double price);
}
//具体策略类
public class NormalStrategy implements UserStrategy{
	@Override
	public double calPrice(double price) {
		return price;
	}
}
public class VIPStrategy implements UserStrategy{
	@Override
	public double calPrice(double price) {
		return price*0.9;
	}
}
//环境类
public class Price {
	//持有一个具体的策略对象
	private UserStrategy strategy;
	public Price(UserStrategy strategy) {
		this.strategy = strategy;
	}
	//策略接口的引用
	public double settlement(double price) {
		return this.strategy.calPrice(price);
	}
}
//测试
public class Test {
	public static void main(String[] args) {
		//选择并创建需要使用的策略对象
		UserStrategy strategy=new VIPStrategy();
		//创建环境
		Price price = new Price(strategy);
		System.out.println(price.settlement(100));
	}
}

Prototype原型模式
将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象;原型模式要求对象实现一个可以“克隆”自身的接口,这样就可以通过复制一个实例对象本身来创建一个新的实例。可以是浅拷贝也可以是深拷贝;
我们先讨论一下Java中的克隆:
Object类提供protected Object clone()方法对对象进行复制,子类当然也可以把这个方法置换掉,提供满足自己需要的复制方法;Cloneable接口的作用是在运行时期通知Java虚拟机可以安全地在这个类上使用clone()方法;
克隆条件:
(1)对任何的对象x,都有:x.clone()!=x;换言之,克隆对象与原对象不是同一个对象;
(2)对任何的对象x,都有:x.clone().getClass() == x.getClass(),换言之,克隆对象与原对象的类型一样;
(3)如果对象x的equals()方法定义其恰当的话,那么x.clone().equals(x)应当成立的;
浅克隆(浅拷贝):只负责克隆按值传递的数据(比如基本数据类型、String类型),而不复制它所引用的对象;
深克隆(深拷贝):克隆按值传递的数据和所引用的对象;即把要复制的对象所引用的对象都复制了一遍;这里会涉及到一个克隆深度的问题,这个取决于引用的对象是深克隆还是浅克隆,所以这里可能会有一个循环克隆的问题;

接下来再具体说下原型模式:
(1)客户(Client)角色:客户类提出创建对象的请求。
(2)抽象原型(Prototype)角色:这是一个抽象角色,通常由一个Java接口或Java抽象类实现。此角色给出所有的具体原型类所需的接口。
(3)具体原型(Concrete Prototype)角色:被复制的对象。此角色需要实现抽象的原型角色所要求的接口。
原型模式有两种表现形式:(1)简单形式(2)登记形式,这两种表现形式仅仅是原型模式的不同实现;登记形式
简单形式:

//抽象原型类
public class People implements Cloneable{
    private String name;
    private Date date;
    public People(String name, Date date) {
	this.name = name;
	this.date = date;
    }
   //重写clone方法,关键在于这里,深拷贝浅拷贝自己选择
    @Override
    protected Object clone(){
    	People p=null;
        try {
		p=(People)super.clone();
	} catch (CloneNotSupportedException e) {
		e.printStackTrace();
	}
        return p;
    }
    //getter、setter省略
}
//具体原型类
public class Student extends People{
	public Student(String name, Date date) {
		super(name, date);
	}
	public void print(){
		System.out.println(this.getName()+"---"+this.getDate());
	}
}
//客户端
public class Test{
	public static void main(String[] args) throws CloneNotSupportedException {
		Student s=new Student("张三",new Date());
		s.print();
		Student s2=(Student)s.clone();
		s2.print();
	}
}

打印结果:
张三—Fri Sep 21 11:20:27 CST 2018
张三—Fri Sep 21 11:20:27 CST 2018

登记形式:多了一个原型管理器(PrototypeManager)角色,该角色的作用是:创建具体原型类的对象,并记录每一个被创建的对象;

//原型管理器
public class PrototypeManager {
	//用来记录原型的编号和原型实例的对应关系
    private static Map<String,People> map = new HashMap<String,People>();
    //私有化构造方法,避免外部创建实例
    private PrototypeManager(){}
    /**
     * 向原型管理器里面添加或是修改某个原型注册
     * @param prototypeId 原型编号
     * @param prototype 原型实例
     */
    public synchronized static void setPrototype(String prototypeId , People p){
        map.put(prototypeId, p);
    }
    /**
     * 从原型管理器里面删除某个原型注册
     * @param prototypeId 原型编号
     */
    public synchronized static void removePrototype(String prototypeId){
        map.remove(prototypeId);
    }
    /**
     * 获取某个原型编号对应的原型实例
     * @param prototypeId    原型编号
     * @return    原型编号对应的原型实例
     * @throws Exception    如果原型编号对应的实例不存在,则抛出异常
     */
    public synchronized static People getPrototype(String prototypeId){
    	People prototype = map.get(prototypeId);
        if(prototype == null){
            System.out.println("您希望获取的原型还没有注册或已被销毁");
        }
        return prototype;
    }
}
//客户端
public class Test{
	public static void main(String[] args) throws CloneNotSupportedException {
		Student s1=new Student("张三",new Date());
		PrototypeManager.setPrototype("s1", s1);
		Student s2=(Student) PrototypeManager.getPrototype("s1").clone();
		s1.print();
		s2.print();
	}
}

对比:如果需要创建的原型对象数目较少而且比较固定的话,建议采用简单形式,对象由客户端保存;反之,建议采用登记形式,对象由管理器保存,并且增加判断管理;

Template模板模式
准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑;不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现;子类可以置换掉父类的可变部分,但是子类却不可以改变模板方法所代表的顶级逻辑;
组成:
抽象模板(Abstract Template):定义了一个或多个抽象操作,以便让子类实现。这些抽象操作叫做基本操作,它们是一个顶级逻辑的组成步骤;定义并实现了一个模板方法,这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法;
具体模板(Concrete Template):实现父类所定义的一个或多个抽象方法,它们是一个顶级逻辑的组成步骤;每一个抽象模板角色都可以有任意多个具体模板角色与之对应,而每一个具体模板角色都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同;

//抽象模板
public abstract class AbstractTemplate {
    //模板方法,可定义多个
    public void templateMethod(){
        //调用基本方法
        abstractMethod();
        hookMethod();
        concreteMethod();
    }
    //基本方法的声明(由子类实现)
    protected abstract void abstractMethod();
    //基本方法(空方法)
    protected void hookMethod(){}
    //基本方法(已经实现)
    private final void concreteMethod(){
        //业务相关的代码
    }
}
//具体模板
public class ConcreteTemplate extends AbstractTemplate{
    //基本方法的实现
    @Override
    public void abstractMethod() {
        //业务相关的代码
    }
    //重写父类的方法
    @Override
    public void hookMethod() {
        //业务相关的代码
    }
}

至此,7种常用的设计模式说明完毕,其他的设计模式以后有时间再补上;
如下为总结对比:
单例模式:整个应用程序中,某个类只有一个实例,且必须自己创建自己的唯一实例,并提供给所有其他对象;
因此适用于需要唯一实例且会被频繁调用(如果实例化的对象长时间不被利用,系统会认为该对象是垃圾而被回收,这可能会导致对象状态的丢失)的环境,例如:多线程共同操作统一文件,一些工具类;推荐使用饿汉式和枚举类;
工厂模式:创建对象接口,让子类选择实例化哪一种对象,因此创建过程在子类进行;适用于需要在不同条件下创建不同实例,例如Hibernate换数据库只需要换方言和驱动;
策略模式:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换,所有具体算法类实现自同一接口或抽象类;例如:出游方式选择,每一种出游方式都是一个策略;
工厂VC策略:都是通过多态实现不同子类的选取,Factory对应Context,从用户出发,工厂仅需要传递对应条件就能得到对象,做了选取工作;策略需要自己创建一个想使用的类对象,然后传递类对象作为参数调用不同算法,不需要做选取工作;

代理模式:用一个类代表另一个类的功能;例如:spring AOP;
委派模式:两个无关系但有一模一样方法属性的类,调用类B等于调用类A同名方法与属性;目的是隐藏具体实现逻辑;(不属于23种经典设计模式之一),例如springmvc框架中的DispatcherServlet;
代理VS委派:代理两个类需要实现同一接口,而委派是用一个类代替另一个类,不涉及接口;

原型模式:使用原型实例进行复制、克隆获取新对象;可以快速的获取批量对象集;例如:一个对象多个修改者情况;
模板模式:主要是抽象通用算法由子类实现,封装不变部分,扩展可变部分;调用将以抽象类中定义的方式进行,因为行为定义在抽象类;例如:spring中对hibernate的支持,封装了开启事务等,扩展了具体逻辑;

猜你喜欢

转载自blog.csdn.net/Dx_wang/article/details/82799182