24种设计模式(二)

24种设计模式

第13章 迭代器模式

第14章 组合模式

第15章 模版方法模式

第16章 策略模式

第17章 状态模式

第18章 备忘录模式

第19章 享元模式

Github 源码下载

第13章 迭代器模式(Iterator)

1.迭代器模式的定义

提供一种方法顺序访问一个聚合对象中的各个元素,而又不需暴露该对象的内部表示。
所谓聚合是指一组对象的组合结构,比如:Java中的集合、数组等。

2. 迭代器模式的结构和说明

image
■ Iterator:迭代器接口。定义访问和遍历元素的接口。

■ ConcreteIterator:具体的迭代器实现对象。实现对聚合对象的遍历,并跟踪遍历时的当前位置。

■ Aggregate:聚合对象。定义创建相应迭代器对象的接口。

■ ConcreteAggregate:具体聚合对象。实现创建相应的迭代器对象。

3. 迭代器模式示例代码
public class IteratorDemo {
    public static void main(String[] args) {
        String[] strArr = {"张飞","关羽","姜维","赵云","马良"} ;
        ConcreteAggregate aggregate = new ConcreteAggregate(strArr);
        Iterator iterator = new ConcreteIterator(aggregate);

        iterator.first();
        while (!iterator.isDone()){
            System.out.println(iterator.currentItem());
            iterator.next();
        }
    }
}
interface Iterator{
    void first();

    void next();

    boolean isDone();

    Object currentItem();
}

class ConcreteIterator implements Iterator{
    private ConcreteAggregate concreteAggregate;

    private int index = -1;
    public ConcreteIterator(ConcreteAggregate concreteAggregate) {
        this.concreteAggregate = concreteAggregate;
    }

    @Override
    public void first() {
        index = 0;
    }

    @Override
    public void next() {
        if(index < concreteAggregate.size()){
            index++;
        }
    }

    @Override
    public boolean isDone() {
        if(index == concreteAggregate.size()){
            return true;
        }
        return false;
    }

    @Override
    public Object currentItem() {
        return concreteAggregate.getObj(index);
    }
}
abstract class Aggregate{
    abstract Iterator createIterator();
}

class ConcreteAggregate extends Aggregate{
    private String[] ss = null ;

    public ConcreteAggregate(String[] ss) {
        this.ss = ss;
    }

    @Override
    Iterator createIterator() {
        return null;
    }

    public Object getObj(int index){
        Object obj = null;
        if(index < ss.length){
            obj = ss[index];
        }
        return obj;
    }

    public int size(){
        int length = 0;
        if(ss!=null){
            length = ss.length;
        }
        return length;
    }
}
4. Java的迭代器

在java.util包里面有一个Iterator的接口,在Java中实现迭代器模式是非常简单的,而且Java的集合框架中的聚合对象基本上都是提供了迭代器的。

6. 双向迭代器

所谓双向迭代器的意思就是:可以同时向前和向后遍历数据的迭代器。
在Java util包中的ListIterator接口就是一个双向迭代器的示例。

7. 迭代器模式的优点
■ 更好的封装性
■ 迭代器模式可以让你访问一个聚合对象的内容,而无须暴露该聚合对象的内部表示,从而提高聚合对象的封装性。
■ 可以以不同的遍历方式来遍历一个聚合
■ 使用迭代器模式,使得聚合对象的内容和具体的迭代算法分离开。
这样就可以通过使用不同的迭代器的实例、不同的遍历方式来遍历一个聚合对象了,比如上面示例的带迭代策略的迭代器。
■ 迭代器简化了聚合的接口 ■ 有了迭代器的接口,则聚合本身就不需要再定义这些接口了,从而简化了聚合的接口定义。
■ 简化客户端调用
■ 迭代器为遍历不同的聚合对象提供了一个统一的接口,使得客户端遍历聚合对象的内容变得更简单。
■ 同一个聚合上可以有多个遍历
■ 每个迭代器保持它自己的遍历状态,比如前面实现中的迭代索引位置,因此可以对同一个聚合对象同时进行多个遍历。
8.何时选用迭代器模式

建议在以下情况中选用迭代器模式。

■ 如果你希望提供访问一个聚合对象的内容,但是又不想暴露它的内部表示的时候,可以使用迭代器模式来提供迭代器接口,从而让客户端只是通过迭代器的接口来访问聚合对象,而无须关心聚合对象的内部实现。

■ 如果你希望有多种遍历方式可以访问聚合对象,可以使用迭代器模式。

■ 如果你希望为遍历不同的聚合对象提供一个统一的接口,可以使用迭代器模式。

9.迭代器模式的本质:控制访问聚合对象中的元素。

第14章 组合模式(Composite)

1.组合模式的定义

将对象组合成树型结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

2. 组合模式的结构和说明

image

■ Component:抽象的组件对象,为组合中的对象声明接口,让客户端可以通过这个接口来访问和管理整个对象结构,可以在里面为定义的功能提供缺省的实现。

■ Leaf:叶子节点对象,定义和实现叶子对象的行为,不再包含其他的子节点对象。

■ Composite:组合对象,通常会存储子组件,定义包含子组件的那些组件的行为,并实现在组件接口中定义的与子组件有关的操作。

■ Client:客户端,通过组件接口来操作组合结构里面的组件对象。

image

3. 组合模式示例代码
import java.util.ArrayList;
import java.util.List;

public class ComponentDemo {
    public static void main(String[] args) {
        Component root = new Composite();

        Component component1 = new Composite();
        Component component2 = new Composite();

        Leaf leaf1 = new Leaf();
        Leaf leaf2 = new Leaf();
        Leaf leaf3 = new Leaf();

        component1.addChild(leaf1);
        component2.addChild(leaf2);

        root.addChild(component1);
        root.addChild(component2);
        root.addChild(leaf3);

        root.someoperation();

    }
}
abstract class Component{
    public abstract void someoperation();

    public void addChild(Component child){
            throw new UnsupportedOperationException("不支持的操作!");

    }

    public void removeChild(Component child){

            throw new UnsupportedOperationException("不支持的操作!");

    }

    public Component getChild(int index){
            throw new UnsupportedOperationException("不支持的操作!");


    }
}

class Composite extends Component{
    List<Component> componentList = new ArrayList<>();
    @Override
    public void someoperation() {
        for (Component component : componentList){
            component.someoperation();
        }
    }

    @Override
    public void addChild(Component child) {
        if(child != null){
            componentList.add(child);
        }
    }

    @Override
    public void removeChild(Component child) {
        if(child != null){
            componentList.remove(child);
        }
    }

    @Override
    public Component getChild(int index) {
        if(index < componentList.size()){
            return componentList.get(index);
        }
        return null;
    }
}
class Leaf extends Component{
    @Override
    public void someoperation() {
        System.out.println("Leaf do something");
    }
}
4. 组合模式的优缺点

组合模式有以下优点。

■ 定义了包含基本对象和组合对象的类层次结构
在组合模式中,基本对象可以被组合成复杂的组合对象,而组合对象又可以组合成更复杂的组合对象,可以不断地递归组合下去,从而构成一个统一的组合对象的类层次结构。

■ 统一了组合对象和叶子对象
在组合模式中,可以把叶子对象当作特殊的组合对象看待,为它们定义统一的父类,从而把组合对象和叶子对象的行为统一起来。

■ 简化了客户端
调用组合模式通过统一组合对象和叶子对象,使得客户端在使用它们的时候,不需要再去区分它们,客户不关心使用的到底是什么类型的对象,这就大大简化了客户端的使用。

■ 更容易扩展
由于客户端是统一地面对Component来操作,因此,新定义的Composite或Leaf子类能够很容易地与已有的结构一起工作,而客户端不需要为增添了新的组件类而改变。

组合模式的缺点是很难限制组合中的组件类型。

容易增加新的组件也会带来一些问题,比如很难限制组合中的组件类型。
这在需要检测组件类型的时候,使得我们不能依靠编译期的类型约束来完成,必须在运行期间动态检测。

5.组合模式的本质

组合模式的本质:统一叶子对象和组合对象。

组合模式目的是让客户端不再区分操作的是组合对象还是叶子对象,而是以一个统一的方式来操作。

组合模式通过把叶子对象当成特殊的组合对象看待,从而对叶子对象和组合对象一视同仁,全部当成了Component对象,有机地统一了叶子对象和组合对象。

正是因为统一了叶子对象和组合对象,在将对象构建成树型结构的时候,才不需要做区分,反正是组件对象里面包含其他的组件对象,如此递归下去;也才使得对于树形结构的操作变得简单,不管对象类型,统一操作。

6.何时选用组合模式

建议在以下情况中选用组合模式。

■ 如果你想表示对象的部分—整体层次结构,可以选用组合模式,把整体和部分的操作统一起来,使得层次结构实现更简单,从外部来使用这个层次结构也容易。

■ 如果你希望统一地使用组合结构中的所有对象,可以选用组合模式,这正是组合模式提供的主要功能。

第15章 模板方法模式(Template Method)

1.模板方法模式的定义

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

2. 模板方法模式的结构和说明

image

■ AbstractClass:抽象类。用来定义算法骨架和原语操作,具体的子类通过重定义这些原语操作来实现一个算法的各个步骤。在这个类里面,还可以提供算法中通用的实现。

■ ConcreteClass:具体实现类。用来实现算法骨架中的某些步骤,完成与特定子类相关的功能。

3. 使用模板方法模式重写示例
public class TemplateDemo {
    public static void main(String[] args) {
        AbstractClass concreteClass = new ConcreteClass();
        concreteClass.doOperation1();
        concreteClass.doOperation2();
    }
}

abstract class AbstractClass{
   abstract void doOperation1();

   abstract void doOperation2();
}

class ConcreteClass extends AbstractClass{
    @Override
    void doOperation1() {
        System.out.println("doOperation1");
    }

    @Override
    void doOperation2() {
        System.out.println("doOperation2");
    }
}
4. 模板方法模式的优缺点

■ 模板方法模式的优点是实现代码复用。
模板方法模式是一种实现代码复用的很好的手段。通过把子类的公共功能提炼和抽取,把公共部分放到模板中去实现。

■ 模板方法模式的缺点是算法骨架不容易升级。
模板方法模式最基本的功能就是通过模板的制定,把算法骨架完全固定下来。事实上模板和子类是非常耦合的,如果要对模板中的算法骨架进行变更,可能就会要求所有相关的子类进行相应的变化。所以抽取算法骨架的时候要特别小心,尽量确保是不会变化的部分才放到模板中。

5.模板方法模式的本质

模板方法模式的本质:固定算法骨架。

模板方法模式主要是通过制定模板,把算法步骤固定下来,至于谁来实现,模板可以自己提供实现,也可以由子类去实现,还可以通过回调机制让其他类来实现。

通过固定算法骨架来约束子类的行为,并在特定的扩展点来让子类进行功能扩展,从而让程序既有很好的复用性,又有较好的扩展性。

6.何时选用模板方法模式

建议在以下情况中选用模板方法模式。

■ 需要固定定义算法骨架,实现一个算法的不变的部分,并把可变的行为留给子类来实现的情况。

■ 各个子类中具有公共行为,应该抽取出来,集中在一个公共类中去实现,从而避免代码重复。

■ 需要控制子类扩展的情况。模板方法模式会在特定的点来调用子类的方法,这样只允许在这些点进行扩展。

第16章 策略模式(Strategy)

1.策略模式的定义

定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

2. 策略模式的结构和说明

image

■ Strategy:策略接口,用来约束一系列具体的策略算法。Context使用这个接口来调用具体的策略实现定义的算法。

■ ConcreteStrategy:具体的策略实现,也就是具体的算法实现。

■ Context:上下文,负责和具体的策略类交互。通常上下文会持有一个真正的策略实现,上下文还可以让具体的策略类来获取上下文的数据,甚至让具体的策略类来回调上下文的方法。

3. 策略模式示例代码
public class StrategyDemo {
    public static void main(String[] args) {
        Strategy strategy = new ConcreteStrategyA();
        Context context = new Context(strategy);
        context.contextInterface();
    }
}
interface Strategy{
    void algorithmInterface();
}

class ConcreteStrategyA implements Strategy{
    @Override
    public void algorithmInterface() {
        System.out.println("algorithm1");
    }
}

class ConcreteStrategyB implements Strategy{
    @Override
    public void algorithmInterface() {
        System.out.println("algorithm2");
    }
}

class ConcreteStrategyC implements Strategy{
    @Override
    public void algorithmInterface() {
        System.out.println("algorithm3");
    }
}
class Context{
    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public void contextInterface(){
        strategy.algorithmInterface();
    }
}
4. 策略模式的优缺点

策略模式有以下优点。

■ 定义一系列算法
策略模式的功能就是定义一系列算法,实现让这些算法可以相互替换。所以会为这一系列算法定义公共的接口,以约束一系列算法要实现的功能。如果这一系列算法具有公共功能,可以把策略接口实现成为抽象类,把这些公共功能实现到父类中。

■ 避免多重条件语句
策略模式的一系列策略算法是平等的,是可以互换的,写在一起就是通过if-else结构来组织,如果此时具体的算法实现中又有条件语句,就构成了多重条件语句,使用策略模式能避免这样的多重条件语句。

■ 更好的扩展性
在策略模式中扩展新的策略实现非常容易,只要增加新的策略实现类,然后在使用策略的地方选择使用这个新的策略实现就可以了。

策略模式有以下缺点。

■ 客户必须了解每种策略的不同
策略模式也有缺点,比如让客户端来选择具体使用哪一个策略,这就需要客户了解所有的策略,还要了解各种策略的功能和不同,这样才能做出正确的选择,而且这样也暴露了策略的具体实现。

■ 增加了对象数目
由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。

■ 只适合扁平的算法结构
策略模式的一系列算法地位是平等的,是可以相互替换的,事实上构成了一个扁平的算法结构,也就是在一个策略接口下,有多个平等的策略算法,就相当于兄弟算法。而且在运行时刻只有一个算法被使用,这就限制了算法使用的层级,使用的时候不能嵌套使用。
对于出现需要嵌套使用多个算法的情况,比如折上折、折后返卷等业务的实现,需要组合或者是嵌套使用多个算法的情况,可以考虑使用装饰模式,或是变形的职责链,或是AOP等方式来实现。

5.策略模式的本质

策略模式的本质:分离算法,选择实现。

6.何时选用策略模式

建议在以下情况中选用策略模式。

■ 出现有许多相关的类,仅仅是行为有差别的情况下,可以使用策略模式来使用多个行为中的一个来配置一个类的方法,实现算法动态切换。

■ 出现同一个算法,有很多不同实现的情况下,可以使用策略模式来把这些“不同的实现”实现成为一个算法的类层次。

■ 需要封装算法中,有与算法相关数据的情况下,可以使用策略模式来避免暴露这些跟算法相关的数据结构。

■ 出现抽象一个定义了很多行为的类,并且是通过多个if-else语句来选择这些行为的情况下,可以使用策略模式来代替这些条件语句。

第17章 状态模式(State)

1.状态模式的定义

允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。

2. 状态模式的结构和说明

image

■ Context:环境,也称上下文,通常用来定义客户感兴趣的接口,同时维护一个来具体处理当前状态的实例对象。

■ State:状态接口,用来封装与上下文的一个特定状态所对应的行为。

■ ConcreteState:具体实现状态处理的类,每个类实现一个跟上下文相关的状态的具体处理。

3. 状态模式示例代码
public class StateDemo {
    public static void main(String[] args) {
        State state = new ConcreteStateA();
        ContextState contextState = new ContextState();
        contextState.setState(state);
        contextState.process();
    }
}

interface State{
    public void handle(String param);
}

class ConcreteStateA implements State{
    @Override
    public void handle(String param) {
        System.out.println("A do something:"+param);
    }
}
class ConcreteStateB implements State{
    @Override
    public void handle(String param) {
        System.out.println("A do something:"+param);
    }
}
class ContextState{
    int i=10;
    private State state;

    public void setState(State state) {
        this.state = state;
    }

    public void process(){

        while (i>0){
            if(i<=5){
                setState(new ConcreteStateB());
            }
            state.handle(i+"");
            i--;
        }

    }
}
4.状态和行为

所谓对象的状态,通常指的就是对象实例的属性的值;而行为指的就是对象的功能,再具体点说,行为大多可以对应到方法上。

状态模式的功能就是分离状态的行为,通过维护状态的变化,来调用不同状态对应的不同功能。

也就是说,状态和行为是相关联的,它们的关系可以描述为:状态决定行为。

由于状态是在运行期被改变的,因此行为也会在运行期根据状态的改变而改变,看起来,同一个对象,在不同的运行时刻,行为是不一样的,就像是类被修改了一样。

5. 状态模式的优缺点

状态模式有以下优点。

■ 简化应用逻辑控制
状态模式使用单独的类来封装一个状态的处理。如果把一个大的程序控制分成很多小块,每块定义一个状态来代表,那么就可以把这些逻辑控制的代码分散到很多单独的状态类中去,这样就把着眼点从执行状态提高到整个对象的状态,使得代码结构化和意图更清晰,从而简化应用的逻辑控制。

对于依赖于状态的if-else,理论上来讲,也可以改变成应用状态模式来实现,把每个if或else块定义一个状态来代表,那么就可以把块内的功能代码移动到状态处理类中,从而减少if-else,避免出现巨大的条件语句。

■ 更好地分离状态和行为
状态模式通过设置所有状态类的公共接口,把状态和状态对应的行为分离开,把所有与一个特定的状态相关的行为都放入一个对象中,使得应用程序在控制的时候,只需要关心状态的切换,而不用关心这个状态对应的真正处理。

■ 更好的扩展性
引入了状态处理的公共接口后,使得扩展新的状态变得非常容易,只需要新增加一个实现状态处理的公共接口的实现类,然后在进行状态维护的地方,设置状态变化到这个新的状态即可。

■ 显式化进行状态转换
状态模式为不同的状态引入独立的对象,使得状态的转换变得更加明确。而且状态对象可以保证上下文不会发生内部状态不一致的情况,因为上下文中只有一个变量来记录状态对象,只要为这一个变量赋值就可以了。

状态模式也有一个很明显的缺点,一个状态对应一个状态处理类,会使得程序引入太多的状态类,这样程序变得杂乱。

6.状态模式的本质

状态模式的本质:根据状态来分离和选择行为。
仔细分析状态模式的结构,如果没有上下文,那么就退化回到只有接口和实现了,正是通过接口,把状态和状态对应的行为分开,才使得通过状态模式设计的程序易于扩展和维护。

而上下文主要负责的是公共的状态驱动,每当状态发生改变的时候,通常都是回调上下文来执行状态对应的功能。当然,上下文自身也可以维护状态的变化,另外,上下文通常还会作为多个状态处理类之间的数据载体,在多个状态处理类之间传递数据。

2.何时选用状态模式

建议在以下情况中选用状态模式。

■ 如果一个对象的行为取决于它的状态,而且它必须在运行时刻根据状态来改变它的行为,可以使用状态模式,来把状态和行为分离开。虽然分离开了,但状态和行为是有对应关系的,可以在运行期间,通过改变状态,就能够调用到该状态对应的状态处理对象上去,从而改变对象的行为。

■ 如果一个操作中含有庞大的多分支语句,而且这些分支依赖于该对象的状态,可以使用状态模式,把各个分支的处理分散包装到单独的对象处理类中,这样,这些分支对应的对象就可以不依赖于其他对象而独立变化了。

第18章 备忘录模式(Memento)

1.备忘录模式的定义

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

一个备忘录是一个对象,它存储另一个对象在某个瞬间的内部状态,后者被称为备忘录的原发器。

2. 备忘录模式的结构和说明

image
■ Memento:备忘录。主要用来存储原发器对象的内部状态,但是具体需要存储哪些数据是由原发器对象来决定的。另外备忘录应该只能由原发器对象来访问它内部的数据,原发器外部的对象不应该访问到备忘录对象的内部数据。

■ Originator:原发器。使用备忘录来保存某个时刻原发器自身的状态,也可以使用备忘录来恢复内部状态。

■ Caretaker:备忘录管理者,或者称为备忘录负责人。主要负责保存备忘录对象,但是不能对备忘录对象的内容进行操作或检查。

3. 备忘录模式示例代码
public class MementoDemo {
    public static void main(String[] args) {
        Originator originator = new Originator();
        originator.setState("404");
        Memento memento = originator.createMemento();

        Caretaker caretaker = new Caretaker();
        caretaker.saveMemento(memento);

        System.out.println(originator.getState());


        originator.setState("500");
        System.out.println(originator.getState());

        Memento memento1 = caretaker.retriveMemento();
        originator.setMemento(memento1);
        System.out.println(originator.getState());
    }
}

interface Memento{

}
class Originator{
    private String state = "";

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

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

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

    private static class MementoImpl implements Memento{
        private String state = "";

        public MementoImpl() {

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

        public String getState() {
            return state;
        }
    }

}

class Caretaker{
    private Memento memento = null;

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

    public Memento retriveMemento(){
        return this.memento;
    }

}
4. 备忘录模式的优缺点

备忘录模式有以下优点。

■ 更好的封装性
备忘录模式通过使用备忘录对象,来封装原发器对象的内部状态,虽然这个对象是保存在原发器对象的外部,但是由于备忘录对象的窄接口并不提供任何方法。

这样有效地保证了对原发器对象内部状态的封装,不把原发器对象的内部实现细节暴露给外部。

■ 简化了原发器
备忘录模式中,备忘录对象被保存到原发器对象之外,让客户来管理他们请求的状态,从而让原发器对象得到简化。

■ 窄接口和宽接口
备忘录模式,通过引入窄接口和宽接口,使得不同的地方,对备忘录对象的访问是不一样的。窄接口保证了只有原发器才可以访问备忘录对象的状态。

备忘录模式的缺点,是可能会导致高开销。

备忘录模式基本的功能,就是对备忘录对象的存储和恢复,它的基本实现方式就是缓存备忘录对象。这样一来,如果需要缓存的数据量很大,或者是特别频繁地创建备忘录对象,开销是很大的。

5.备忘录模式的本质

备忘录模式的本质:保存和恢复内部状态。

备忘录模式备忘的就是原发器对象的内部状态,这些内部状态是不对外的,只有原发器对象才能够进行操作。
标准的备忘录模式保存数据的手段是,通过内存缓存,广义的备忘录模式实现的时候,可以采用离线存储的方式,把这些数据保存到文件或者数据库等地方。

6.何时选用备忘录模式

建议在以下情况中选用备忘录模式。

■ 如果必须保存一个对象在某一个时刻的全部或者部分状态,方便在以后需要的时候,可以把该对象恢复到先前的状态,可以使用备忘录模式。使用备忘录对象来封装和保存需要保存的内部状态,然后把备忘录对象保存到管理者对象中,在需要的时候,再从管理者对象中获取备忘录对象,来恢复对象的状态。

■ 如果需要保存一个对象的内部状态,但是如果用接口来让其他对象直接得到这些需要保存的状态,将会暴露对象的实现细节并破坏对象的封装性,这时可以使用备忘录模式,把备忘录对象实现成为原发器对象的内部类,而且还是私有的,从而保证只有原发器对象才能访问该备忘录对象。这样既保存了需要保存的状态,又不会暴露原发器对象的内部实现细节。

第19章 享元模式(Flyweight)

1.享元模式的定义

运用共享技术有效地支持大量细粒度的对象。

2. 享元模式的结构和说明

image

■ Flyweight:享元接口,通过这个接口Flyweight可以接受并作用于外部状态。通过这个接口传入外部的状态,在享元对象的方法处理中可能会使用这些外部的数据。

■ ConcreteFlyweight:具体的享元实现对象,必须是可共享的,需要封装Flyweight的内部状态。

■ UnsharedConcreteFlyweight:非共享的享元实现对象,并不是所有的Flyweight实现对象都需要共享。非共享的享元实现对象通常是对共享享元对象的组合对象。

■ FlyweightFactory:享元工厂,主要用来创建并管理共享的享元对象,并对外提供访问共享享元的接口。

■ Client:享元客户端,主要的工作是维持一个对Flyweight的引用,计算或存储享元对象的外部状态,当然这里可以访问共享和不共享的Flyweight对象。

3. 享元模式示例代码
import java.util.HashMap;
import java.util.Map;

public class FlyweightDemo {
    public static void main(String[] args) {
        FlyweightFactory flyweightFactory = new FlyweightFactory();
        Flyweight flyweight = flyweightFactory.getFlyweight("404");
        flyweight.opreation("Error");

        flyweight = new UnsharedConcreteFlyweight();
        flyweight.opreation("500");
    }
}
interface Flyweight{
    void opreation(String outState);
}
class ConcreteFlyweight implements Flyweight{
    private String innerState;

    public ConcreteFlyweight(String innerState) {
        this.innerState = innerState;
    }

    @Override
    public void opreation(String outState) {
        System.out.println("do someting:"+innerState+", "+outState);
    }
}
class UnsharedConcreteFlyweight implements Flyweight{
    private String ownState;

    @Override
    public void opreation(String outState) {
        System.out.println("do something:"+outState);
    }
}

class FlyweightFactory{
    Map<String,Flyweight> flyweightMap = new HashMap<String,Flyweight> ();

    public Flyweight getFlyweight(String key){
        Flyweight f = flyweightMap.get(key);

        if(f == null ){
            f = new ConcreteFlyweight(key);
            flyweightMap.put(key,f);
        }
        return f;
    }
}
4. 享元模式的优缺点

享元模式的优点是:减少对象数量,节省内存空间。

可能有的朋友认为共享对象会浪费空间,但是如果这些对象频繁使用,那么其实是节省空间的。因为占用空间的大小等于每个对象实例占用的大小再乘以数量,对于享元对象来讲,基本上就只有一个实例,大大减少了享元对象的数量,并节省不少的内存空间。

节省的空间取决于以下几个因素:因为共享而减少的实例数目、每个实例本身所占用的空间。假如每个对象实例占用2个字节,如果不共享数量是100个,而共享后就只有一个了,那么节省的空间约等于(100-1)×2字节。

享元模式的缺点是:维护共享对象,需要额外开销。

如同前面演示的享元工厂,在维护共享对象的时候,如果功能复杂,会有很多额外的开销,比如有一个线程来维护垃圾回收。

5.享元模式的本质

享元模式的本质:分离与共享。

分离的是对象状态中变与不变的部分,共享的是对象中不变的部分。享元模式的关键之处就在于分离变与不变,把不变的部分作为享元对象的内部状态,而变化部分则作为外部状态,由外部来维护,这样享元对象就能够被共享,从而减少对象数量,并节省大量的内存空间。

6.何时选用享元模式

建议在以下情况中选用享元模式。

■ 如果一个应用程序使用了大量的细粒度对象,可以使用享元模式来减少对象数量。

■ 如果由于使用大量的对象,造成很大的存储开销,可以使用享元模式来减少对象数量,并节约内存。

■ 如果对象的大多数状态都可以转变为外部状态,比如通过计算得到,或是从外部传入等,可以使用享元模式来实现内部状态和外部状态的分离。

■ 如果不考虑对象的外部状态,可以用相对较少的共享对象取代很多组合对象,可以使用享元模式来共享对象,然后组合对象来使用这些共享对象。

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值