常用
创建型设计模式
单例模式
定义:确保单例类只有一个实例,并且这个单例类提供一个函数接口让其他类获取到这个唯一的实例。
单例模式共有5种写法:
1、 饿汉模式
public class Singleton {
private static Singleton instance = new Singleton;
private Singleton () {
}
public static Singleton getInstance() {
return instance;
}
}
复制代码
- 在类加载的时候就完成实例化,如果从始至终未使用这个实例,则会造成内存的浪费。
2、懒汉模式(线程安全)
public class Singletion {
private static Singleton instance;
private Singleton () {
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
复制代码
- 为了处理并发,每次调用getInstance方法时都需要进行同步,会有不必要的同步开销。
3、双重检查模式(DCL)
public class Singleton {
private static volatile Singleton instance;
private Singleton {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
复制代码
- 第一次判空,省去了不必要的同步。第二次是在Singleton等于空时才创建实例。
- 使用volatile保证了实例的可见性。
- DCL在一定程度上解决了资源的消耗和多余的同步、线程安全等问题,但是在某些情况下会失效。
假设线程A执行到instance = new Singleton()
语句,看起来只有一行代码,但实际上它并不是原子操作,这句代码最终会被编译成多条汇编指令,它大致做了3件事:
1)给instance的实例分配内存。
2)调用Singleton()构造函数,初始化成员字段。
3)将instance对象指向分配的内存空间(此时instance就不是null了)。
但是,由于Java编译器允许处理器乱序执行,以及JDK1.5之前JMM中的Cache、寄存器到主内存回写顺序的规定,上面的2和3的顺序是无法保证的,也就是说,执行顺序可能是1-2-3也可能是1-3-2。如果是后者,并且在3执行完毕、2未执行之前,被切换到线程B上,这时候instance因为已经在线程A内执行过了3,instance已经是非空了,所以,线程B直接取走instance,再使用时就会出错,这就是DCL失效问题,而且这种难以跟踪难以重现的错误可能会隐藏很久。
在JDK1.5之后,SUN官方已经注意到这种问题,调整了JVM,具体化了volatile关键字,因此,如果JDK1.5或之后的版本,只需要将instance的定义改成private volatile static Singleton instance = null就可以保证instance对象每次都是从主内存中读取,就可以使用DCL的写法来完成单例模式。当然,volatile或多或少也会影响到性能,但考虑到程序的正确性,这点牺牲也是值得的。
DCL优点:资源利用率高,第一次执行getInstance时单例对象才会被实例化,效率高。
缺点:第一次加载稍慢,也由于JMM的原因导致偶尔会失败。在高并发环境下也有一定的缺陷,虽然发生概率很小。DCL模式是使用最多的单例实现方式,它能够在需要时才实例化对象,并且能在绝大多数场景下保证对象的唯一性,除非你的代码在并发场景比较复杂或低于JDK1.6版本下使用,否则,这种方式一般能够满足要求。
4、静态内部类单例模式
public class Singleton() {
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.sInstance;
}
private static class SingletonHolder {
private static final Singleton sInstance = new Singleton();
}
}
复制代码
- 第一次调用getInstance方法时虚拟机才加载SingletonHolder并初始化sInstance,这样保证了线程安全和实例的唯一性。
5、枚举单例
public enum Singleton {
INSTANCE;
public void doSomeThing() {
}
}
复制代码
- 默认枚举实例的创建是线程安全的,并且在任何情况下都是单例。
- 简单、可读性不高。
注意:上面的几种单例模式创建的单例对象被反序列化时会重新创建实例,可以重写readReslove方法返回当前的单例对象。
简单工厂模式
定义:也称为静态工厂方法模式,由一个工厂对象决定创建出哪一种产品类的实例。
简单工厂模式中有如下角色:
- 工厂类:核心,负责创建所有实例的内部逻辑,由外界直接调用。
- 抽象产品类:要创建所有对象的抽象父类,负责描述所有实例所共有的公共接口。
- 具体产品类:要创建的产品。
public abstract class Computer {
public abstarct void start();
}
public class LenovaComputer extends Computer {
@Override
public void start() {
...
}
}
public class HpComputer extends Computer {
@Override
public void start() {
...
}
}
public class AsusComputer extends Computer {
@Override
public void start() {
...
}
}
public class ComputerFactory {
public static Computer createComputer(String type) {
Computer mComputer = null;
switch (type) {
case "lenovo":
mComputer = new LenovoComputer();
break;
case "hp":
mComputer = new HpComputer();
break;
case "asus":
mComputer = new AsusComputer();
break;
}
return mComputer;
}
}
复制代码
工厂方法模式
定义:定义一个创建对象的接口,让子类决定实例化哪个类。
工厂方法有以下角色:
- 抽象产品类。
- 具体产品类。
- 抽象工厂类:返回一个泛型的产品对象。
- 具体工厂类:返回具体的产品对象。
public abstract class ComputerFactory {
public abstract <T extends Computer> T createComputer(Class<T> clz);
}
public class GDComputerFactory extends ComputerFactory {
@Override
public <T extends Computer> T createComputer(Class<T> clz) {
Computer computer = null;
String classname = clz.getName();
try {
computer = (Computer) Class.forName(classname).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return (T) computer;
}
}
复制代码
相比简单工厂,如果我们需要新增产品类,无需修改工厂类,直接创建产品即可。
建造者模式
将一个复杂对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示。
建造者有以下角色:
- 导演类:负责安排已有模块的安装顺序,最后通知建造者开始建造。
- 建造者:抽象Builder类,用于规范产品的组建。
- 具体建造者:实现抽象Builder类的所有方法,并返回建造好的对象。
- 产品类。
public class Computer {
private String mCpu;
private Stiring mMainboard;
private String mRam;
public void setmCpu(String mCpu) {
this.mCpu = mCpu;
}
public void setmMainboard(String mMainboard) {
this.mMainboard = mMainboard;
}
public void setmRam(String mRam) {
this.mRam = mRam;
}
}
public abstract class Builder {
public abstract void buildCpu(String cpu);
public abstract void buildMainboard(String mainboard);
public abstract void buildRam(String ram);
public abstract Computer create();
}
public class MoonComputerBuilder extends Builder {
private Computer mComputer = new Computer();
@Override
public void buildCpu(String cpu) {
mComputer.setmCpu(cpu);
}
@Override
public void buildMainboard(String mainboard) {
mComputer.setmMainboard(mainboard);
}
@Override
public void buildRam(String ram) {
mComputer.setmRam(ram);
}
@Override
public Computer create() {
return mComputer;
}
}
public class Director {
Builder mBuilder = null;
public Director (Builder builder) {
this.mBuilder = builder;
}
public Computer createComputer(String cpu, String mainboard, String ram) {
this.mBuilder.buildCpu(cpu);
this.mBuilder.buildMainboard(mainboard);
this.mBuilder.buildRam(ram);
return mBuilder.create();
}
}
复制代码
结构型设计模式
代理模式
定义:给某对象提供一个代理对象,并由代理对象控制对原对象的引用。
代理模式的结构:
-
抽象对象角色:声明了目标对象和代理对象的共同接口,这样一来在任何可以使用目标对象的地方都可以使用代理对象。
-
目标对象角色:定义了代理对象所代表的目标对象。
-
代理对象角色:代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象;代理对象提供一个与目标对象相同的接口,以便可以在任何时候替代目标对象。代理对象通常在客户端调用传递给目标对象之前或之后,执行某个操作,而不是单纯地将调用传递给目标对象。
public interface IObject {
//操作
public void operation();
}
public class RealObject implements IObject {
@Override
public void operation() {
//一些操作
System.out.println("一些操作");
}
}
public class ProxyObject implements IObject{
RealObject realObject = new RealObject();
@Override
public void operation() {
//调用目标对象之前可以做相关操作
System.out.println("before");
realObject.operation();
//调用目标对象之后可以做相关操作
System.out.println("after");
}
}
复制代码
动态代理
在代码运行时通过反射来动态地生成代理类的对象,并确定到底来代理谁。
public class DynamicObject implements InvocationHandler {
private Object obj;
public DynamicObject(Object obj) {
this.obj = obj;
}
@Overrdie
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(obj, args);
}
}
IObject object = new RealObject();
DynamicObject mDynamicObject = new DynamicObject(object);
ClassLoader cl = object.getClass.getClassLoader();
IObject objecting = Proxy.newProxyInstance(cl, new Class[]{IObject.class}, mDynamicObject);
objecting.operation();
复制代码
真实主题类发生变化时,由于它实现了公用的接口,因此代理类不需要修改。
装饰模式
定义:动态地给一个对象添加一些额外的职责,是继承关系的一个替代方案。
装饰模式有以下角色:
- 抽象组件:接口/抽象类,被装饰的最原始的对象。
- 具体组件:被装饰的具体对象。
- 抽象装饰者:扩展抽象组件的功能。
- 具体装饰者:装饰者具体实现类。
public interface Component {
public void sampleOperation();
}
public class ConcreteComponent implements Component {
@Override
public void sampleOperation() {
// 写相关的业务代码
}
}
public class Decorator implements Component{
private Component component;
public Decorator(Component component){
this.component = component;
}
@Override
public void sampleOperation() {
// 委派给构件
component.sampleOperation();
}
}
public class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
public void sampleOperation2() {
}
@Override
public void sampleOperation() {
     super.sampleOperation();
     sampleOperation2();
}
}
public class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}
public void sampleOperation2() {
}
@Override
public void sampleOperation() {
     super.sampleOperation();
     sampleOperation2();
}
}
复制代码
- 使用组合,动态地扩展对象的功能,在运行时能够使用不同的装饰器实现不同的行为。
- 比继承更易出错,旨在必要时使用。
外观模式
定义:一个子系统的内部和外部通信必须通过一个统一的对象进行。即提供一个高层的接口,方便子系统更易于使用。
外观模式有以下角色:
- 外观类:将客户端的请求代理给适当的子系统对象。
- 子系统类:可以有一个或多个子系统,用于处理外观类指派的任务。注意子系统不含外观类的引用。
public class ModuleA {
//示意方法
public void testA(){
System.out.println("调用ModuleA中的testA方法");
}
}
public class ModuleB {
//示意方法
public void testB(){
System.out.println("调用ModuleB中的testB方法");
}
}
public class ModuleC {
//示意方法
public void testC(){
System.out.println("调用ModuleC中的testC方法");
}
}
public class Facade {
//示意方法,满足客户端需要的功能
public void test(){
ModuleA a = new ModuleA();
a.testA();
ModuleB b = new ModuleB();
b.testB();
ModuleC c = new ModuleC();
c.testC();
}
}
复制代码
- 将对子系统的依赖转换为对外观类的依赖。
- 对外部隐藏子系统的具体实现。
- 这种外观特性增强了安全性。
享元模式
定义:使用共享对象有效支持大量细粒度(性质相似)的对象。
额外的两个概念:
-
1、内部状态:共享信息,不可改变。
-
2、外部状态:依赖标记,可以改变。 享元模式有以下角色:
-
抽象享元角色:定义对象内部和外部状态的接口。
-
具体享元角色:实现抽象享元角色的任务。
-
享元工厂:管理对象池及创建享元对象。
public interface IGoods {
public void showGoodsPrice(String name);
}
public class Goods implements IGoods {
private String name;
private String price;
Goods (String name) {
this.name = name;
}
@Override
public void showGoodsPrice(String name) {
...
}
}
public class GoodsFactory {
private static Map<String, Goods> pool = new HashMap<String, Goods>();
public static Goods getGoods(String name) {
if (pool.containsKey(name)) {
return pool.get(name);
} else {
Goods goods = new Goods(name);
pool.put(name, goods);
return goods;
}
}
}
复制代码
Goods goods1 = GoodsFactory.getGoods("Android进阶之光");
goods1.showGoodsPrice("普通版");
Goods goods2 = GoodsFactory.getGoods("Android进阶之光");
goods1.showGoodsPrice("普通版");
Goods goods3 = GoodsFactory.getGoods("Android进阶之光");
goods1.showGoodsPrice("签名版");
复制代码
goods1为新创建的对象,后面的都是从对象池中取出的缓存对象。
适配器模式
定义:将一个接口转换为另一个需要的接口。
适配器模式有类的适配器模式和对象的适配器模式两种不同的形式。
类适配器模式
类的适配器模式把适配的类的API转换成为目标类的API。
模式所涉及的角色有:
- 目标(Target)角色:这就是所期待得到的接口。注意:由于这里讨论的是类适配器模式,因此目标不可以是类。
- 源(Adapee)角色:现在需要适配的接口。
- 适配器(Adaper)角色:适配器类是本模式的核心。适配器把源接口转换成目标接口。显然,这一角色不可以是接口,而必须是具体类。
public interface Target {
/**
* 这是源类Adaptee也有的方法
*/
public void sampleOperation1();
/**
* 这是源类Adapteee没有的方法
*/
public void sampleOperation2();
}
public class Adaptee {
public void sampleOperation1(){}
}
public class Adapter extends Adaptee implements Target {
/**
* 由于源类Adaptee没有方法sampleOperation2()
* 因此适配器补充上这个方法
*/
@Override
public void sampleOperation2() {
//写相关的代码
}
}
复制代码
对象适配器模式
与类的适配器模式一样,对象的适配器模式把被适配的类的API转换成为目标类的API,与类的适配器模式不同的是,对象的适配器模式不是使用继承关系连接到Adaptee类,而是使用委派关系连接到Adaptee类。
public interface Target {
/**
* 这是源类Adaptee也有的方法
*/
public void sampleOperation1();
/**
* 这是源类Adapteee没有的方法
*/
public void sampleOperation2();
}
public class Adaptee {
public void sampleOperation1(){}
}
public class Adapter {
private Adaptee adaptee;
public Adapter(Adaptee adaptee){
this.adaptee = adaptee;
}
/**
* 源类Adaptee有方法sampleOperation1
* 因此适配器类直接委派即可
*/
public void sampleOperation1(){
this.adaptee.sampleOperation1();
}
/**
* 源类Adaptee没有方法sampleOperation2
* 因此由适配器类需要补充此方法
*/
public void sampleOperation2(){
//写相关的代码
}
}
复制代码
适配器模式的优点
- 更好的复用性
系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。
- 更好的扩展性
在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。
适配器模式的缺点
过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
行为型设计模式
策略模式
定义:定义一系列的算法,将每一个算法都封装起来,并且可相互替换。这使得算法可以独立于调用者而单独变化。
策略模式有以下角色:
- 上下文角色:用来操作策略使用的上下文环境。屏蔽了高层模块对策略和算法的直接访问。
- 抽象策略角色。
- 具体策略角色。
public interface FightingStrategy {
public void fighting();
}
public class WeakRivalStrategy implements FightingStrategy {
@Override
public void fighting() {
...
}
}
public class CommonRivalStrategy implements FightingStrategy {
@Override
public void fighting() {
...
}
}
public class StrongRivalStrategy implements FightingStrategy {
@Override
public void fighting() {
...
}
}
public class Context {
private FightingStrategy mFightingStrategy;
public void Context(FightingStrategy fightingStrategy) {
this.mFightingStrategy = fightingStrategy;
}
public void fighting() {
mFightingStrategy.fighting();
}
}
复制代码
Context context;
context = new Context(new WeakRivalStrategy());
context.fighting();
context = new Context(new CommonRivalStategy());
context.fighting();
context = new Context(new StrongRivalStategy());
context.fighting();
复制代码
- 隐藏具体策略中算法的实现细节。
- 避免使用多重条件语句。
- 易于扩展
- 每一个策略都是一个类,复用性小。
- 上层模块必须知道有哪些策略类,与迪米特原则相违背。
模板方法模式
定义:定义了一套算法框架,将某些步骤交给子类去实现。使得子类不需改变框架结构即可重写算法中的某些步骤。
模板方法模式有以下角色:
- 抽象类:定义了一套算法框架。
- 具体实现类。
public abstract class AbstractSwordsman {
public final void fighting() {
neigong();
// 这个是具体方法
jingmai();
if (hasWeapons()) {
weapons();
}
moves();
hook();
}
protected void hook() { };
protected void abstract neigong();
protected void abstract weapons();
protected void abstract moves();
public void jingmai() {
...
}
protected boolean hasWeapons() {
return ture;
}
}
public class ZhangWuJi extends AbstractSwordsman {
@Override
public void neigong() {
...
}
@Override
public void weapons() {
// 没有武器,不做处理
}
@Override
public void moves() {
...
}
@Override
public boolean hasWeapons() {
return false;
}
}
punlc class ZhangSanFeng extends AbstractSwordsman {
@Override
public void neigong() {
...
}
@Override
public void weapons() {
...
}
@Override
public void moves() {
...
}
@Override
public void hook() {
// 额外处理
...
}
}
复制代码
- 可以使用hook方法实现子类对父类的反向控制。
- 可以把核心或固定的逻辑搬移到基类,其它细节交给子类实现。
- 每个不同的实现都需要定义一个子类,复用性小。
观察者模式
定义:定义对象间一种一对多的依赖关系,使得当每一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。
观察者模式的结构
- 被观察者:从类图中可以看到,类中有一个用来存放观察者对象的Vector容器(之所以使用Vector而不使用List,是因为多线程操作时,Vector在是安全的,而List则是不安全的),这个Vector容器是被观察者类的核心,另外还有三个方法:attach方法是向这个容器中添加观察者对象;detach方法是从容器中移除观察者对象;notify方法是依次调用观察者对象的对应方法。这个角色可以是接口,也可以是抽象类或者具体的类,因为很多情况下会与其他的模式混用,所以使用抽象类的情况比较多。
- 观察者:观察者角色一般是一个接口,它只有一个update方法,在被观察者状态发生变化时,这个方法就会被触发调用。
- 具体的被观察者:使用这个角色是为了便于扩展,可以在此角色中定义具体的业务逻辑。
- 具体的观察者:观察者接口的具体实现,在这个角色中,将定义被观察者对象状态发生变化时所要处理的逻辑。
abstract class Subject {
private Vector<Observer> obs = new Vector<Observer>();
public void addObserver(Observer obs){
this.obs.add(obs);
}
public void delObserver(Observer obs){
this.obs.remove(obs);
}
protected void notifyObserver(){
for(Observer o: obs){
o.update();
}
}
public abstract void doSomething();
}
class ConcreteSubject extends Subject {
public void doSomething(){
System.out.println("被观察者事件反生");
this.notifyObserver();
}
}
interface Observer {
public void update();
}
class ConcreteObserver1 implements Observer {
public void update() {
System.out.println("观察者1收到信息,并进行处理。");
}
}
class ConcreteObserver2 implements Observer {
public void update() {
System.out.println("观察者2收到信息,并进行处理。");
}
}
复制代码
观察者模式的优点
观察者与被观察者之间是属于轻度的关联关系,并且是抽象耦合的,这样,对于两者来说都比较容易进行扩展。
观察者模式是一种常用的触发机制,它形成一条触发链,依次对各个观察者的方法进行处理。但同时,这也算是观察者模式一个缺点,由于是链式触发,当观察者比较多的时候,性能问题是比较令人担忧的。并且,在链式结构中,比较容易出现循环引用的错误,造成系统假死。
非常用
迭代器模式
定义:提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部细节。
迭代器模式的结构
- 抽象容器:一般是一个接口,提供一个iterator()方法,例如java中的Collection接口,List接口,Set接口等。
- 具体容器:就是抽象容器的具体实现类,比如List接口的有序列表实现ArrayList,List接口的链表实现LinkList,Set接口的哈希列表的实现HashSet等。
- 抽象迭代器:定义遍历元素所需要的方法,一般来说会有这么三个方法:取得第一个元素的方法first(),取得下一个元素的方法next(),判断是否遍历结束的方法isDone()(或者叫hasNext()),移出当前对象的方法remove(),
- 迭代器实现:实现迭代器接口中定义的方法,完成集合的迭代。
interface Iterator {
public Object next();
public boolean hasNext();
}
class ConcreteIterator implements Iterator{
private List list = new ArrayList();
private int cursor =0;
public ConcreteIterator(List list){
this.list = list;
}
public boolean hasNext() {
if(cursor==list.size()){
return false;
}
return true;
}
public Object next() {
Object obj = null;
if(this.hasNext()){
obj = this.list.get(cursor++);
}
return obj;
}
}
interface Aggregate {
public void add(Object obj);
public void remove(Object obj);
public Iterator iterator();
}
class ConcreteAggregate implements Aggregate {
private List list = new ArrayList();
public void add(Object obj) {
list.add(obj);
}
public Iterator iterator() {
return new ConcreteIterator(list);
}
public void remove(Object obj) {
list.remove(obj);
}
}
复制代码
- 迭代器模式的优点有:
简化了遍历方式,对于对象集合的遍历,还是比较麻烦的,对于数组或者有序列表,我们尚可以通过游标来取得,但用户需要在对集合了解很清楚的前提下,自行遍历对象,但是对于hash表来说,用户遍历起来就比较麻烦了。而引入了迭代器方法后,用户用起来就简单的多了。 可以提供多种遍历方式,比如说对有序列表,我们可以根据需要提供正序遍历,倒序遍历两种迭代器,用户用起来只需要得到我们实现好的迭代器,就可以方便的对集合进行遍历了。 封装性良好,用户只需要得到迭代器就可以遍历,而对于遍历算法则不用去关心。
- 迭代器模式的缺点:
对于比较简单的遍历(像数组或者有序列表),使用迭代器方式遍历较为繁琐,大家可能都有感觉,像ArrayList,我们宁可愿意使用for循环和get方法来遍历集合。
责任连模式
定义:使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
责任连模式的结构
- 抽象处理类:抽象处理类中主要包含一个指向下一处理类的成员变量nextHandler和一个处理请求的方法handRequest,handRequest方法的主要主要思想是,如果满足处理的条件,则有本处理类来进行处理,否则由nextHandler来处理。
- 具体处理类:具体处理类主要是对具体的处理逻辑和处理的适用条件进行实现。
class Level {
private int level = 0;
public Level(int level){
this.level = level;
};
public boolean above(Level level){
if(this.level >= level.level){
return true;
}
return false;
}
}
class Request {
Level level;
public Request(Level level){
this.level = level;
}
public Level getLevel(){
return level;
}
}
class Response {
}
abstract class Handler {
private Handler nextHandler;
public final Response handleRequest(Request request){
Response response = null;
if(this.getHandlerLevel().above(request.getLevel())){
response = this.response(request);
}else{
if(this.nextHandler != null){
this.nextHandler.handleRequest(request);
}else{
System.out.println("-----没有合适的处理器-----");
}
}
return response;
}
public void setNextHandler(Handler handler){
this.nextHandler = handler;
}
protected abstract Level getHandlerLevel();
public abstract Response response(Request request);
}
class ConcreteHandler1 extends Handler {
protected Level getHandlerLevel() {
return new Level(1);
}
public Response response(Request request) {
System.out.println("-----请求由处理器1进行处理-----");
return null;
}
}
class ConcreteHandler2 extends Handler {
protected Level getHandlerLevel() {
return new Level(3);
}
public Response response(Request request) {
System.out.println("-----请求由处理器2进行处理-----");
return null;
}
}
class ConcreteHandler3 extends Handler {
protected Level getHandlerLevel() {
return new Level(5);
}
public Response response(Request request) {
System.out.println("-----请求由处理器3进行处理-----");
return null;
}
}
复制代码
代码中Level类是模拟判定条件;Request,Response分别对应请求和响应;抽象类Handler中主要进行条件的判断,这里模拟一个处理等级,只有处理类的处理等级高于Request的等级才能处理,否则交给下一个处理者处理。在Client类中设置好链的前后执行关系,执行时将请求交给第一个处理类,这就是责任连模式。
责任链模式的优缺点
责任链模式与if…else…相比,他的耦合性要低一些,因为它把条件判定都分散到了各个处理类中,并且这些处理类的优先处理顺序可以随意设定。责任链模式也有缺点,这与if…else…语句的缺点是一样的,那就是在找到正确的处理类之前,所有的判定条件都要被执行一遍,当责任链比较长时,性能问题比较严重。
访问者模式
定义:封装某些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
访问者模式的结构:
- 抽象访问者:抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法中的参数定义哪些对象是可以被访问的。
- 访问者:实现抽象访问者所声明的方法,它影响到访问者访问到一个类后该干什么,要做什么事情。
- 抽象元素类:接口或者抽象类,声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义的。抽象元素一般有两类方法,一部分是本身的业务逻辑,另外就是允许接收哪类访问者来访问。
- 元素类:实现抽象元素类所声明的accept方法,通常都是visitor.visit(this),基本上已经形成一种定式了。
- 结构对象:一个元素的容器,一般包含一个容纳多个不同类、不同接口的容器,如List、Set、Map等,在项目中一般很少抽象出这个角色。
abstract class Element {
public abstract void accept(IVisitor visitor);
public abstract void doSomething();
}
interface IVisitor {
public void visit(ConcreteElement1 el1);
public void visit(ConcreteElement2 el2);
}
class ConcreteElement1 extends Element {
public void doSomething(){
System.out.println("这是元素1");
}
public void accept(IVisitor visitor) {
visitor.visit(this);
}
}
class ConcreteElement2 extends Element {
public void doSomething(){
System.out.println("这是元素2");
}
public void accept(IVisitor visitor) {
visitor.visit(this);
}
}
class ObjectStruture {
public static List<Element> getList(){
List<Element> list = new ArrayList<Element>();
Random ran = new Random();
for(int i=0; i<10; i++){
int a = ran.nextInt(100);
if(a>50){
list.add(new ConcreteElement1());
}else{
list.add(new ConcreteElement2());
}
}
return list;
}
}
复制代码
访问者模式的优点
- 符合单一职责原则:凡是适用访问者模式的场景中,元素类中需要封装在访问者中的操作必定是与元素类本身关系不大且是易变的操作,使用访问者模式一方面符合单一职责原则,另一方面,因为被封装的操作通常来说都是易变的,所以当发生变化时,就可以在不改变元素类本身的前提下,实现对变化部分的扩展。
- 扩展性良好:元素类可以通过接受不同的访问者来实现对不同操作的扩展。
组合模式
定义:将对象组成成树形结构,以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
安全式组合模式的结构
安全模式的组合模式要求管理聚集的方法只出现在树枝构件类中,而不出现在树叶构件类中。 这种形式涉及到三个角色:
-
抽象构件(Component)角色:这是一个抽象角色,它给参加组合的对象定义出公共的接口及其默认行为,可以用来管理所有的子对象。合成对象通常把它所包含的子对象当做类型为Component的对象。在安全式的合成模式里,构件角色并不定义出管理子对象的方法,这一定义由树枝构件对象给出。
-
树叶构件(Leaf)角色:树叶对象是没有下级子对象的对象,定义出参加组合的原始对象的行为。
-
树枝构件(Composite)角色:代表参加组合的有下级子对象的对象。树枝构件类给出所有的管理子对象的方法,如add()、remove()以及getChild()。
public interface Component {
/**
* 输出组建自身的名称
*/
public void printStruct(String preStr);
}
public class Composite implements Component {
/**
* 用来存储组合对象中包含的子组件对象
*/
private List<Component> childComponents = new ArrayList<Component>();
/**
* 组合对象的名字
*/
private String name;
/**
* 构造方法,传入组合对象的名字
* @param name 组合对象的名字
*/
public Composite(String name){
this.name = name;
}
/**
* 聚集管理方法,增加一个子构件对象
* @param child 子构件对象
*/
public void addChild(Component child){
childComponents.add(child);
}
/**
* 聚集管理方法,删除一个子构件对象
* @param index 子构件对象的下标
*/
public void removeChild(int index){
childComponents.remove(index);
}
/**
* 聚集管理方法,返回所有子构件对象
*/
public List<Component> getChild(){
return childComponents;
}
/**
* 输出对象的自身结构
* @param preStr 前缀,主要是按照层级拼接空格,实现向后缩进
*/
@Override
public void printStruct(String preStr) {
// 先把自己输出
System.out.println(preStr + "+" + this.name);
//如果还包含有子组件,那么就输出这些子组件对象
if(this.childComponents != null){
//添加两个空格,表示向后缩进两个空格
preStr += " ";
//输出当前对象的子对象
for(Component c : childComponents){
//递归输出每个子对象
c.printStruct(preStr);
}
}
}
}
public class Leaf implements Component {
/**
* 叶子对象的名字
*/
private String name;
/**
* 构造方法,传入叶子对象的名称
* @param name 叶子对象的名字
*/
public Leaf(String name){
this.name = name;
}
/**
* 输出叶子对象的结构,叶子对象没有子对象,也就是输出叶子对象的名字
* @param preStr 前缀,主要是按照层级拼接的空格,实现向后缩进
*/
@Override
public void printStruct(String preStr) {
// TODO Auto-generated method stub
System.out.println(preStr + "-" + name);
}
}
复制代码
树枝构件类(Composite)给出了addChild()、removeChild()以及getChild()等方法的声明和实现,而树叶构件类则没有给出这些方法的声明或实现。这样的做法是安全的做法,由于这个特点,客户端应用程序不可能错误地调用树叶构件的聚集方法,因为树叶构件没有这些方法,调用会导致编译错误。
安全式合成模式的缺点是不够透明,因为树叶类和树枝类将具有不同的接口。
透明式组合模式的结构
与安全式的合成模式不同的是,透明式的合成模式要求所有的具体构件类,不论树枝构件还是树叶构件,均符合一个固定接口。
public abstract class Component {
/**
* 输出组建自身的名称
*/
public abstract void printStruct(String preStr);
/**
* 聚集管理方法,增加一个子构件对象
* @param child 子构件对象
*/
public void addChild(Component child){
/**
* 缺省实现,抛出异常,因为叶子对象没有此功能
* 或者子组件没有实现这个功能
*/
throw new UnsupportedOperationException("对象不支持此功能");
}
/**
* 聚集管理方法,删除一个子构件对象
* @param index 子构件对象的下标
*/
public void removeChild(int index){
/**
* 缺省实现,抛出异常,因为叶子对象没有此功能
* 或者子组件没有实现这个功能
*/
throw new UnsupportedOperationException("对象不支持此功能");
}
/**
* 聚集管理方法,返回所有子构件对象
*/
public List<Component> getChild(){
/**
* 缺省实现,抛出异常,因为叶子对象没有此功能
* 或者子组件没有实现这个功能
*/
throw new UnsupportedOperationException("对象不支持此功能");
}
}
public class Composite extends Component {
/**
* 用来存储组合对象中包含的子组件对象
*/
private List<Component> childComponents = new ArrayList<Component>();
/**
* 组合对象的名字
*/
private String name;
/**
* 构造方法,传入组合对象的名字
* @param name 组合对象的名字
*/
public Composite(String name){
this.name = name;
}
/**
* 聚集管理方法,增加一个子构件对象
* @param child 子构件对象
*/
public void addChild(Component child){
childComponents.add(child);
}
/**
* 聚集管理方法,删除一个子构件对象
* @param index 子构件对象的下标
*/
public void removeChild(int index){
childComponents.remove(index);
}
/**
* 聚集管理方法,返回所有子构件对象
*/
public List<Component> getChild(){
return childComponents;
}
/**
* 输出对象的自身结构
* @param preStr 前缀,主要是按照层级拼接空格,实现向后缩进
*/
@Override
public void printStruct(String preStr) {
// 先把自己输出
System.out.println(preStr + "+" + this.name);
//如果还包含有子组件,那么就输出这些子组件对象
if(this.childComponents != null){
//添加两个空格,表示向后缩进两个空格
preStr += " ";
//输出当前对象的子对象
for(Component c : childComponents){
//递归输出每个子对象
c.printStruct(preStr);
}
}
}
}
public class Leaf extends Component {
/**
* 叶子对象的名字
*/
private String name;
/**
* 构造方法,传入叶子对象的名称
* @param name 叶子对象的名字
*/
public Leaf(String name){
this.name = name;
}
/**
* 输出叶子对象的结构,叶子对象没有子对象,也就是输出叶子对象的名字
* @param preStr 前缀,主要是按照层级拼接的空格,实现向后缩进
*/
@Override
public void printStruct(String preStr) {
// TODO Auto-generated method stub
System.out.println(preStr + "-" + name);
}
}
复制代码