定义

享元模式是对象的结构模式,享元模式以共享的方式高效的支持大量的细粒度对象,主要用于减少创建对象的数量,以减少内存占用和提高性能

享元对象能做到共享的关键在于区分了内蕴状态和外蕴状态

内蕴状态是存储在享元对象内部的,并且是不会随环境的改变而有所不同,因此,一个享元对象可以具有内蕴状态并且可以共享

外蕴状态是随着环境的改变而改变的,并且是不可以共享的状态,享元对象的外蕴状态必须是由客户端保存,并且是在享元对象创建之后,在需要使用的时候再传到享元对象内部

外蕴状态不可以影响享元对象的内蕴状态,换句话说,它们是相互独立的

意图

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

主要解决问题

在有大量对象时,有可能会造成内存溢出,我们把其***同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建

何时使用

系统中存在大量的对象,并且消耗了大量的内存,这些对象的状态大部分可以外部化,这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替

优缺点

优点: 大大减少对象的创建,降低系统的内存,使效率提高

缺点: 使得系统更加复杂,为了使对象可以共享,需要将一些状态外部化

结构

 

涉及的角色:

  • 抽象享元(Flyweight)角色:所有具体享元类的超类,为这些类规定需要实现的接口
  • 具体享元(ConcreteFlyweight)角色:实现抽象享元角色规定的接口,如果有内蕴状态的话,必须为内蕴状态提供存储空间,享元对象的内蕴状态必须与所处的环境无关,从而使得享元对象可以在系统***存
  • 享元(FlyweightFactory)工厂角色:负责创建和管理享元角色,本角色必须保证享元对象可以被系统适当的共享,当客户端调用享元对象的时候,享元工厂会检查系统中是不是已经有了符合要求的享元对象,如果有了就提供这个对象,如果没有享元工厂就应当创建一个合适的享元对象
  • 客户端(Client)角色:本角色需要维护一个对所有享元对象的引用,需要自行存储所有享元对象的外蕴状态

对应源码如下:

public abstract class Flyweight {
    /** 一个示意性的方法,参数state是外蕴状态 */
    public abstract void operation(String state);
}
public class ConcreteFlyweight extends Flyweight {
    private int intrinsicState;
    public ConcreteFlyweight(int intrinsicState) {
        this.intrinsicState = intrinsicState;
    }    /**
     * 外蕴状态作为参量传入
     * 改变方法的行为
     * 并不改变对象的内蕴状态
     */
    @Override
    public void operation(String state) {
        System.out.println("内蕴状态:" + intrinsicState + ",,外蕴状态:" + state);
    }}
public class FlyweightFactory {
    private Map<String, Flyweight> map = new HashMap();
    private Flyweight flyweight;    public FlyweightFactory() {    }    //内蕴状态作为参量传入    public Flyweight factory(int state) {
        if (map.containsKey(state)) {
            return map.get(state);
        } else {
            Flyweight fly = new ConcreteFlyweight(state);
            map.put(String.valueOf(state), fly);
            return fly;
        }    }    public void checkFlyweight() {        Flyweight fly;        int i = 0;
        Iterator iterator = map.entrySet().iterator();        while (iterator.hasNext()) {
            Map.Entry e = (Map.Entry) iterator.next();            System.out.println("Item    " + (++i) + ":" + e.getKey());
        }    }}
public class Client {
    public static void main(String[] args) {
        FlyweightFactory factory = new FlyweightFactory();
        Flyweight flyweight = factory.factory(888);
        flyweight.operation("第一次");
        flyweight = factory.factory(999);
        flyweight.operation("第二次");
        flyweight = factory.factory(888);
        flyweight.operation("第三次");
        System.out.println();
        factory.checkFlyweight();    }}

 

奶茶摊位的例子

在这个奶茶摊位上,有一系列口味的奶茶,客人来了之后拿到奶茶付了钱就走了,奶茶有内蕴状态,也就是它的口味,没有外部环境影响,也就是没有外蕴状态

如果在系统中,对每一杯奶茶都创建一个对象的话,那么就会创建出很多细小的对象出来,如果按照奶茶的口味区分,每一种口味的奶茶都只创建一个对象,然后共享

下面使用代码实现: 抽象享元角色,也就是每一种奶茶口味需要实现的接口:

public abstract class Order {
    /** 将咖啡卖给客人 */
    public abstract void sellingCoffee();
    /** 返回奶茶的口味 */
    public abstract String getFlavor();
}

具体享元角色,奶茶口味实现抽象享元角色提供的接口:

public class Flavor extends Order {
    private String flavor;
    public Flavor(String flavor) {
        this.flavor = flavor;
    }    @Override
    public void sellingCoffee() {
        System.out.println("将奶茶卖给客人,奶茶口味为:" + this.flavor);
    }    @Override
    public String getFlavor() {
        return this.flavor;
    }}

摊主提供奶茶,也就是享元工厂,负责创建和管理享元角色:

public class FlavorFactory {
    private Order[] flavors = new Flavor[10];
    private int ordersMade = 0;
    private int totalFlavors = 0;
    /** 根据所需要的口味提供奶茶 */
    public Order getOrder(String flavor) {
        if (ordersMade > 0) {
            for (int i=0; i<ordersMade; i++) {
                if (flavor.equals(flavors[i].getFlavor())) {
                    return flavors[i];
                }            }        }        flavors[ordersMade] = new Flavor(flavor);
        totalFlavors++;        return flavors[ordersMade++];
    }    /** 返回所有的奶茶口味 */
    public int getTotalFlavors() {
        return totalFlavors;
    }}

客户端:

public class Client {
    private static Order[] flavors = new Flavor[10];
    private static int ordersMade = 0;
    private static  FlavorFactory factory;
    private static void taskOrder(String flavor) {
        flavors[ordersMade++] = factory.getOrder(flavor);    }    public static void main(String[] args) {
        factory = new FlavorFactory();
        taskOrder("原味");
        taskOrder("香蕉");
        taskOrder("原味");
        taskOrder("草莓");
        taskOrder("草莓");
        for (int i=0; i<ordersMade; i++) {
            flavors[i].sellingCoffee();        }        System.out.println("总共买奶茶人数:" + ordersMade);
        System.out.println("总共卖出奶茶口味数:" + factory.getTotalFlavors());
    }}

 

可以看到,虽然客人买了5杯奶茶,但是奶茶的口味只有三种

奶茶店的例子

奶茶摊位没有提供桌椅给客人,也就是没有外部环境影响,现在奶茶店提供了桌椅,那么桌椅就是外蕴状态

抽象享元角色,也就是每一种奶茶口味需要实现的接口:

public abstract class Order {
    /** 将咖啡卖给客人 */
    public abstract void sellingCoffee(Table table);
    /** 返回奶茶的口味 */
    public abstract String getFlavor();
}

具体享元角色,奶茶口味实现抽象享元角色提供的接口:

public class Flavor extends Order {
    private String flavor;
    public Flavor(String flavor) {
        this.flavor = flavor;
    }    @Override
    public void sellingCoffee(Table table) {
        System.out.println("将奶茶卖给客人,奶茶口味为:" + this.flavor + ",客人所坐桌子号码为:" + table.getNumber());
    }    @Override
    public String getFlavor() {
        return this.flavor;
    }}

奶茶店提供奶茶,也就是享元工厂,负责创建和管理享元角色:

public class FlavorFactory {
    private Order[] flavors = new Flavor[10];
    private int ordersMade = 0;
    private int totalFlavors = 0;
    /** 根据所需要的口味提供奶茶 */
    public Order getOrder(String flavor) {
        if (ordersMade > 0) {
            for (int i=0; i<ordersMade; i++) {
                if (flavor.equals(flavors[i].getFlavor())) {
                    return flavors[i];
                }            }        }        flavors[ordersMade] = new Flavor(flavor);
        totalFlavors++;        return flavors[ordersMade++];
    }    /** 返回所有的奶茶口味 */
    public int getTotalFlavors() {
        return totalFlavors;
    }}

环境角色,桌椅:

public class Table {
    /** 桌子号码 */
    private int number;
    public Table(int number) {
        this.number = number;
    }    public int getNumber() {
        return number;
    }    public void setNumber(int number) {
        this.number = number;
    }}

客户端:

public class Client {
    private static Order[] flavors = new Flavor[10];
    private static int ordersMade = 0;
    private static FlavorFactory factory;
    private static void taskOrder(String flavor) {
        flavors[ordersMade++] = factory.getOrder(flavor);    }    public static void main(String[] args) {
        factory = new FlavorFactory();
        taskOrder("原味");
        taskOrder("香蕉");
        taskOrder("原味");
        taskOrder("草莓");
        taskOrder("草莓");
        for (int i=0; i<ordersMade; i++) {
            flavors[i].sellingCoffee(new Table(i+1));
        }        System.out.println("总共买奶茶人数:" + ordersMade);
        System.out.println("总共卖出奶茶口味数:" + factory.getTotalFlavors());
    }}

 

在什么情况下使用享元模式

  • 一个系统中有大量的对象
  • 这些对象消耗大量的内存
  • 这些对象的状态中大部分都可以外部化
  • 这些对象可以按照内蕴状态分为很多组,当把外蕴状态从对象中剔除时,每一个组都可以用一个对象代替
  • 软件系统不依赖这些对象的身份,即这些对象可以是不可分辨的

作者:狐言不胡言

链接:https://juejin.im/post/6863804279870717965

来源:掘金