Ericson's blog Time is limited, To be a better better man

单例模式

单例模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点。通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象,一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。单例模式结构图如下所示: singleton Singleton类定义了一个GetInstance方法,允许客户访问它的唯一实例,GetInstance是一个静态方法,主要负责创建自己的唯一实例。

class Singleton{
    private static Singleton instance;
    private Singleton(){}
    public static Singleton GetInstance(){
        if(instance==null){
            instance=new Singleton();
        }
        return instance;
    }
}

上述代码在多线程的情况下,会创建多个对象,当有多于两个线程同时进入instance==null的判断中,就会创建多个对象,所以代码要加上线程同步。

class Singleton{
    private static Singleton instance;
    private static final Object syncLock=new Object();
    private Singleton(){}
    public static Singleton GetInstance(){
        synchronized(syncLock){
            if(instance==null){
               instance=new Singleton();
            }
        }
        return instance;
    }
}

上述代码保证了线程同步,但当一个线程进入同步块时其他线程会一直等待进入线程释放资源,效率较低,所以代码不需要对所有线程都加锁,这称为双重锁定。

class Singleton{
    private static Singleton instance;
    private static final Object syncLock=new Object();
    private Singleton(){}
    public static Singleton GetInstance(){
        if(instance==null){
            synchronized(syncLock){
                if(instance==null){
                    instance=new Singleton();
                }
            }
        }
        return instance;
    }
}

还有一种静态初始化的方法实现单例模式,在全局变量声明时初始化,也就是在类加载时就已经初始化了,称为饿汉式单例类,而上述方法在使用时初始化,称为懒汉式单例类。

class Singleton{
    private static final Singleton instance=new Singleton();
    private Singleton(){}
    public static Singleton GetInstance(){
        return instance;
    }
}

迭代器模式

迭代器模式,提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。迭代器模式结构图如下所示: iterator Iterator迭代器抽象类:

abstract class Iterator{
    public abstract Object First();
    public abstract Object Next();
    public abstract boolean IsDone();
    public abstract Object CurrentItem();
}

Aggregate聚集抽象类:

abstract class Aggregate{
    public abstract Iterator CreateIterator();
}

ConcreteIterator具体迭代器类,继承Iterator:

class ConcreteIterator extends Iterator{
    private ConcreteAggregate aggregate;
    private int current=0;
    public ConcreteIterator(ConcreteAggregate aggregate){
        this.aggregate=aggregate;
    }
    public Object First(){
        return aggregate[0];
    }
    public Object Next(){
        Object ret=null;
        current++;
        if(current<aggregate.Count){
            ret=aggregate[current];
        }
        return ret;
    }
    public boolean IsDone(){
        return current>=aggregate.Count?true:false;
    }
    public Object CurrentItem(){
        return aggregate[current];
    }
}

ConcreteAggregate具体聚集类,继承Aggregate:

class ConcreteAggregate extends Aggregate{
    private List<Object> items=new ArrayList<Object>();
    public Iterator CreateIterator(){
        return new ConcreteIterator(this);
    }
    public int Count(){
        return items.size();
    }
    public Object this[int index]{
        get{return items[index];}
        set{items.Insert(index,value);}
    }
}

迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器来负责,这样既可以做到不暴露集合的内部结构,又可以让外部代码透明地访问集合内部的数据。

组合模式

组合模式,将对象组合成树形结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。组合模式的结构图如下所示: composite Component为组合中的对象声明接口,在适当情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component的子部件。代码如下:

abstract class Component{
    protected String name;
    public Component(String name){
        this.name=name;
    }
    public abstract void Add(Component c);
    public abstract void Remove(Component c);
    public abstract void Display(int depth);
}

Leaf在组合中表示叶节点对象,叶节点没有子节点。

class Leaf extends Component{
    public Leaf(String name){
        super(name);
    }
    public void Add(Component c){
        System.out.println("Cannot add to a leaf");
    }
    public void Remove(Component c){
        System.out.println("Cannot remove from a leaf");
    }
    public void Display(int depth){
        System.out.println("-"+depth+" "+name);
    }
}

Composite定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关的操作,代码:

class Composite extends Component{
    private List<Component> children=new ArrayList<Component>();
    public Composite(String name){
        super(name);
    }
    public void Add(Component c){
        children.Add(c);
    }
    public void Remove(Component c){
        children.Remove(c);
    }
    public void Display(int depth){
        System.out.println("-"+depth+" "+name);
        for(Component component:children){
            component.Display(depth+2);
        }
    }
}

组合模式的优点:

  • 可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,使得增加新构件也更容易
  • 客户端调用简单,客户端可以一致的使用组合结构或其中单个对象
  • 定义了包含叶子对象和容器对象的类层次结构,叶子对象可以被组合成更复杂的容器对象,而这个容器对象又可以被组合,这样不断递归下去,可以形成复杂的树形结构
  • 更容易在组合体内加入对象构件,客户端不必因为加入了新的对象构件而更改原有代码

缺点:

  • 是设计变得更加抽象,对象的业务规则如果很复杂,则实现组合模式具有很大的挑战性,而且不是所有的方法都与叶子对象子类都有关联

组合模式总结:组合模式用于将多个对象组合成树形结构以表示“整体-部分”的结构层次,组合模式对单个对象(叶子对象)和组合对象(容器对象)的使用具有一致性;组合对象的关键在于它定义了一个抽象构建类,它既可表示叶子对象,也可表示容器对象,客户仅仅需要针对这个抽象构建进行编程,无需知道他是叶子对象还是容器对象,都是一致对待;组合模式虽然能够非常好地处理层次结构,也使得客户端程序变得简单,但是它也使得设计变得更加抽象,而且也很难对容器中的构件类型进行限制,这会导致在增加新的构件时会产生一些问题。

备忘录模式

备忘录模式,在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。备忘录模式结构图如下所示: memento Originator:负责创建一个备忘录Memento,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态。Originator可根据需要决定Memento存储Originator的哪些内部状态。

Memento:负责存储Originator对象的内部状态,并可防止Originator以外的其他对象访问备忘录Memento。备忘录有两个接口,Caretaker只能看到备忘录的窄接口,它只能讲备忘录传递给其他对象。Originator能够看到一个宽接口,允许它访问赶回到先前状态所需的所有数据。

Caretaker:负责保存好备忘录Memento,不能对备忘录的内容进行操作或检查。

代码如下:

public class Originator {
    private String state;

    public Memento createMemento() {
        return new Memento(state);
    }

    public void setMemento(Memento memento) {
        state = memento.getState();
    }

    public void show() {
        System.out.println("State=" + state);
    }

    public static void main(String[] args) {
        Originator o = new Originator();
        o.state = "On";
        o.show();
        Caretaker c = new Caretaker();
        c.setMemento(o.createMemento());
        o.state = "OFF";
        o.show();
        o.setMemento(c.getMemento());
        o.show();
    }
}

class Memento {
    private String state;

    public Memento(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }
}

class Caretaker {
    private Memento memento;

    public Memento getMemento() {
        return memento;
    }

    public void setMemento(Memento memento) {
        this.memento = memento;
    }
}

备忘录模式的优缺点:
优点:

  • 给用户提供一种可以恢复状态的机制,可以是用户能够比较方便地回到某个历史的状态
  • 实现了信息的封装,使得用户不需要关心状态的保存细节

缺点:

  • 消耗资源,如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存

模式适用场景:

  • 需要保存一个对象在某一时刻的状态或分布状态
  • 如果用一个接口来让其他对象得到这些状态,将会暴露对象的实现细节并破坏对象的封装性,一个对象不希望外界直接访问其内部状态,通过负责人可以间接的访问其内部状态

模式总结:备忘录模式可以实现在不破坏封装的前提下,捕获一个类的内部状态,并且在该对象之外保存该对象的状态,保证该对象能够恢复到历史的某个状态;备忘录模式实现了内部状态的封装,除了创建它的原发器之外其他对象都不能够访问它;备忘录模式会占用较多的内存,消耗资源。

适配器模式

适配器模式,将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。适配器模式又分为类适配器模式和对象适配器模式。适配器模式结构图如下所示: adapter 模式总结:
优点:

  • 通过适配器,客户端可以调用同一接口,因而对客户端来说是透明的,这样做更简单,更直接,更紧凑。
  • 复用了现存的类,解决了现存类和复用环境要求不一致的问题。
  • 将目标类和适配器类解耦,通过引入一个适配器类重用现有的适配器类,而无需修改原有代码
  • 一个对象适配器可以把多个不同的适配器类适配到同一个目标,也就是说,同一个适配器可以把适配器类和它的子类都适配到目标接口。

缺点:

  • 对于对象适配器来说,更换适配器的实现过程比较复杂

适用场景:

  • 系统需要使用现有的类,而这些类的接口不符合系统的接口
  • 想要建立一个可以重用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作
  • 两个类所做的事情相同或相似,但是具有不同接口的时候
  • 旧的系统开发的类已经实现了一些功能,但是客户端却只能以另外接口的形式访问,但我们不希望手动更改原有类的时候
  • 使用第三方组件,组件接口定义和自己定义的不同,不希望修改自己的接口,但是要使用第三方组件接口的功能