Java设计模式之结构型模式-享元模式(Flyweight)

  如何理解享元模式?“享”即共享,“元”指的是元件,也就是小颗粒的东西,“享元”顾名思义便是共享小部件。很多的系统或者程序包含大量的对象,但是这些对象绝大多数都是差不多的,除了一些极个别的属性外。那么也就是说,在一个系统或者程序中有多个相同的对象的时候,我们只需要共享一份就可以了,不必去实例化每一个对象。

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

四个要素

  1. Flyweight: 是所有具体享元类的超类或者接口,通过这个接口Flyweight可以接受并且作用于外部状态。
  2. ConcreteFlyweight: 继承Flyweight超类或者实现Flyweight接口的类,并为内部状态增加存储空间。
  3. UnsharedConcreteFlyweight: 并不需要共享的Flyweight的子类。Flyweight接口使得共享成为了可能,但是它并不强制共享。
  4. FlyweightFactory: 创建并且管理Flyweight对象。确保合理的共享Flyweight。当用户请求一个Flyweight对象时,FlyweightFactory会去做检查,如果找到请求的Flyweight对象则返回该对象,否则将创建一个新的对象并存储供后续使用。

内部状态和外部状态的区分

  内部状态: 可以共享,状态不会随着环境改变而改变
  外部状态: 不可以共享,状态会随着环境改变而改变

示例

  我就拿买衣服举个例子。比方说去商城买衬衫(Shirt)、卫衣(Sweater)、休闲裤(Slacks)这些服饰(我们此处仅考虑服饰的颜色和大小)。不管服饰的颜色如何、大小如何,最后所买下服饰的类型只有三种。

实现方式

  1. 创建一个享元接口 Clothes

public interface Clothes {
    void buy(UnsharedConcreteClothes unsharedConcreteClothes);
}

  2. 创建具体的享元类 ConcreteClothes

public class ConcreteClothes implements Clothes {

    private String name;

    private String size;

    private String color;

    public ConcreteClothes(String name) {
        this.name = name;
    }

    public void setSize(String size) {
        this.size = size;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public void buy(UnsharedConcreteClothes unsharedConcreteClothes) {
        System.out.println("ConcreteClothes [name=" + name + ", size=" + size + ", color=" + color + ", price="
                + unsharedConcreteClothes.getPrice() + "]");
    }

}

  3. 创建非共享享元实现类 UnsharedConcreteClothes

public class UnsharedConcreteClothes implements Clothes {

    private String price;

    public UnsharedConcreteClothes(String price) {
        this.price = price;
    }

    public String getPrice() {
        return price;
    }

    public void setPrice(String price) {
        this.price = price;
    }

    @Override
    public void buy(UnsharedConcreteClothes unsharedConcreteClothes) {
        System.out.println("UnsharedConcreteClothes [price=" + price + "]");
    }

}

  4. 创建享元工厂类 ClothesFactory

public class ClothesFactory {

    private static final HashMap clothesMap = new HashMap<>();

    public static Clothes getClothes(String name) {
        ConcreteClothes clothes = (ConcreteClothes)clothesMap.get(name);

        if (clothes == null) {
            System.out.println("Instance of name = " + name + " does not exists, creating it!");
            clothes = new ConcreteClothes(name);
            System.out.println("Instance of name = " + name + " created!");
            clothesMap.put(name, clothes);
        }
        return clothesMap.get(name);
    }

    public static void getClothesTypeCount() {
        System.out.println("Type of clothes is " + clothesMap.size() + " !");
    }
}

  5. 创建享元测试类 FlyweightTest

public class FlyweightTest {

    private static final String[] name = { "Shirt", "Sweater", "Slacks" };

    private static final String[] size = { "S", "M", "L", "XL", "XXL" };

    private static final String[] color = { "Khaki", "ArmyGreen", "White", "Black" };

    private static final String[] price = { "¥99", "¥129", "¥149", "¥199" };

    public static String getRandom(String[] strs) {
        return strs[new Random().nextInt(strs.length)];
    }

    public static void main(String[] args) {

        for (int i = 0; i < 8; i++) {
            ConcreteClothes concreteClothes = (ConcreteClothes) ClothesFactory.getClothes(getRandom(name));
            concreteClothes.setSize(getRandom(size));
            concreteClothes.setColor(getRandom(color));
            UnsharedConcreteClothes unsharedConcreteClothes = new UnsharedConcreteClothes(getRandom(price));
            concreteClothes.buy(unsharedConcreteClothes);
        }

        ClothesFactory.getClothesTypeCount();
    }
}

运行结果如下:

Instance of name = Slacks does not exists, creating it!
Instance of name = Slacks created!
ConcreteClothes [name=Slacks, size=L, color=Black, price=¥99]
Instance of name = Shirt does not exists, creating it!
Instance of name = Shirt created!
ConcreteClothes [name=Shirt, size=L, color=Black, price=¥99]
ConcreteClothes [name=Slacks, size=S, color=ArmyGreen, price=¥199]
Instance of name = Sweater does not exists, creating it!
Instance of name = Sweater created!
ConcreteClothes [name=Sweater, size=L, color=ArmyGreen, price=¥129]
ConcreteClothes [name=Shirt, size=M, color=White, price=¥99]
ConcreteClothes [name=Shirt, size=XL, color=White, price=¥199]
ConcreteClothes [name=Sweater, size=L, color=White, price=¥99]
ConcreteClothes [name=Shirt, size=L, color=Khaki, price=¥199]
Type of clothes is 3 !

  本例中,name、size、color这些个是可以共享的,可以认为是内部状态。而price可能会受到季节、打折活动等外部环境变化的影响,可以认定为是外部状态。
  在Java语言当中,String类型就使用了享元模式。我们知道String对象是final修饰的,也就是说对象一旦创建就不能改变。在Java中字符串常量都是存在常量池中的,Java会去确保一个字符串常量在常量池中仅有一个拷贝。

优点

  • 共享相同或者相似的细粒度对象,大大减少对象的创建,降低系统的内存,使效率提高
  • 外部状态相对独立,使得对象可以在不同的环境中被复用

缺点

  • 读取外部状态使得运行时间变长
  • 内部状态与外部状态分离,使得程序的逻辑复杂化

你可能感兴趣的:(设计模式)