Press "Enter" to skip to content

设计模式—享元模式

享元模式(Flyweight)是一种结构型设计模式,它的出现是为了在使用大量细粒度对象时节省空间。在使用大量细粒度对象时,它们彼此之间往往有所相同,又有所不同。比如在一个PPT文档中,可能多处出现了“A”这个字母。它们之间的共同处是字形一样,都是A;不同是颜色不同,有的地方字体颜色为黑,有的地方为灰。

我们把这些对象之间相同的地方抽象出来称为“内蕴状态”,不同的地方称为“外蕴状态”。在使用享元模式时,内蕴状态相同的共用一个对象,不同的外蕴状态由使用该对象的客户端传入,并在此对象的方法中体现。也就是说,我们让同一个对象(保存了内蕴状态“A”)创建黑色的“A”和灰色的“A”,但是在创建时由客户端向创建方法中传入字体颜色(外蕴状态),这样能够大量节省空间。

在享元模式中享元工厂有至关重要的作用,它维护了一个共用享元对象池,防止享元对象的重复,并为客户端提供需要的享元对象。也就是说,所有已经被创建的字形对象被存入享元工厂,客户端向工厂申请某一个字形,如果已经存在,取出返回;如果不存在,创建并存入对象池,然后将其返回。

享元模式又分为简单享元模式复合享元模式。复合享元模式中多了不共用的复合享元对象,它其实是由共用的享元对象构成的。复合享元对象对应了这样一种情况,我打算创建一段颜色统一的文字,所以先将文字(每个不同的文字对应一个简单享元对象)放入复合享元对象,再在进行创建时传入字体颜色,这一创建实际是由复合享元对象依次调用它的简单享元对象并传入颜色完成的。

简单享元模式包括:
1. 抽象享元接口
2. 共用的享元类
3. 享元工厂
4. 客户端

复合享元模式包括:
1. 抽象享元接口
2. 共用的享元类
3. 不共用的复合享元类
4. 享元工厂
5. 客户端

示例代码如下:

package DesignPattern;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
//抽象享元接口
interface Prototype {
    void operation(String color);
}
//简单享元具体类
class SimpleFlyweight implements Prototype {
    //word为内蕴状态
    Character word;
    SimpleFlyweight(Character _word) {
        word = _word;
    }
    //color为外蕴状态
    public void operation(String color) {
        System.out.println(word + "(" + color + ")" + " ");
    }
}
//复合享元具体类
class CompositeFlyweight implements Prototype {
    List<SimpleFlyweight> lst = new ArrayList<>();
    void add(SimpleFlyweight obj) {
        lst.add(obj);
    }
    public void operation(String color) {
        for(SimpleFlyweight n : lst) {
            n.operation(color);
        }
    }
}
//享元工厂
class Factory {
    Map<Character,SimpleFlyweight> map = new HashMap<>();
    int getSize() { return map.size(); }
    SimpleFlyweight factory(Character word) {
        SimpleFlyweight res = map.get(word);
        if(res == null) {
            res = new SimpleFlyweight(word);
            map.put(word,res);
        }
        return res;
    }
    CompositeFlyweight factory(List<Character> lst) {
        CompositeFlyweight res = new CompositeFlyweight();
        for(Character ch : lst) {
            res.add(this.factory(ch));
        }
        return res;
    }
}
//客户端
public class Flyweight {
    public static void main(String[] args) {
        Factory fac = new Factory();
        //简单享元模式测试
        SimpleFlyweight obj1 = fac.factory('A');
        SimpleFlyweight obj2 = fac.factory('A');
        obj1.operation("blue");
        obj2.operation("red");
        //size=1,obj1=obj2,可见两个A共用了享元对象
        System.out.println(fac.getSize());
        System.out.println(obj1 == obj2);
        //符合享元测试
        List<Character> lst = new ArrayList<>();
        lst.add('A');
        lst.add('A');
        lst.add('A');
        lst.add('A');
        CompositeFlyweight obj3 = fac.factory(lst);
        CompositeFlyweight obj4 = fac.factory(lst);
        obj3.operation("green");
        obj4.operation("black");
        //size=1,可见复合享元对象底层共用了简单享元对象
        //obj3!=obj4,可见复合享元对象本身不共享
        System.out.println(fac.getSize());
        System.out.println(obj3 == obj4);
    }
}
/*
output:
A(blue) 
A(red) 
1
true
A(green) 
A(green) 
A(green) 
A(green) 
A(black) 
A(black) 
A(black) 
A(black) 
1
false
*/

Be First to Comment

发表评论

电子邮件地址不会被公开。 必填项已用*标注

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax