软件设计模式学习(十五)享元模式


当系统中存在大量相同或类似的对象时,享元模式是一种较好的解决方案,它经过共享技术实现相同或类似的细粒度对象的复用,从而节约内存空间。享元模式提供了一个享元池用于存储已经建立好的享元对象,并经过享元工厂类将享元对象提供给客户端使用。java


模式动机

使用面向对象技术开发时,不少状况下须要在系统中增长类和对象的个数,而且这些对象有些是相同或类似的。当对象太多时,将致使运行代价太高,性能降低等问题。为了不系统中出现大量相同或类似的对象,享元模式经过共享技术实现相同或类似对象的重用,相同的对象都指向一个实例,存储这个实例的对象称为享元池。编程


模式设计

系统中有些对象并不彻底相同,而只是类似,所以须要先找出这些对象的共同点,在享元类中封装这些共同的内容。不一样的内容能够经过外部应用程序来设置,而不进行共享,在享元模式中能够共享的相同内容称为内部状态,而那些须要外部环境设置的不能共享的内容称为外部状态。网络

在享元模式中一般会出现工厂模式,须要建立一个享元工厂来维护一个享元池,用于存储具备相同内部状态的享元对象。实际使用中,可以共享的内部状态是有限的,所以享元对象通常都设计为较小的对象,它所包含的内部状态较少,这种状态通常称为细粒度对象。享元模式的目的就是使用共享技术来实现大量细粒度对象的复用。性能


模式定义

运用共享技术有效地支持大量细粒度对象的复用。系统只使用少许的对象,而这些对象都很类似,状态变化很小,能够实现对象的屡次复用。因为享元模式要求可以共享的对象必须是细粒度对象,所以又称为轻量级模式。测试


模式结构

在这里插入图片描述

  1. Flyweight(抽象享元类)this

    抽象享元类声明是一个接口,经过它能够接受并做用于外部状态。在抽象享元类定义了具体享元类公共的方法,这些方法能够向外界提供享元对象的内部数据(内部状态),同时也能够经过这些方法来设置外部数据(外部状态)设计

  2. ConcreteFlyweight(具体享元类)3d

    具体享元类实现了抽象享元接口,保存了内部状态,具体享元对象是能够共享的。能够结合单例模式来设计享元具体类,为每个具体享元类提供惟一的享元对象。code

  3. UnsharedConcreteFlyweight(非共享具体享元类)对象

    不能被共享的抽象享元类的子类被设计为非共享具体享元类。当须要一个非共享具体享元类的对象时能够直接经过实例化建立。在某些享元模式的层次结构中,非共享具体享元对象还能够将具体享元对象做为子节点。

  4. FlyweightFactory(享元工厂类)

    享元工厂类用于建立并管理享元对象,它针对抽象享元类编程,将各类类型的具体享元对象存储在一个享元池中。当用户请求一个具体享元对象时,享元工厂提供一个存储在享元池已建立的实例或者建立一个实例,返回该新建立的实例并将其存储在享元池中。


模式分析

享元模式的核心在于享元工厂类,享元工厂类的做用在于提供一个用于存储享元对象的享元池。典型的享元工厂类代码以下:

public class FlyweightFactory {

    private HashMap flyweights = new HashMap();

    public Flyweight getFlyweight(String key) {
        if (flyweights.containsKey(key)) {
            return (Flyweight) flyweights.get(key);
        } else {
            Flyweight fw = new ConcreteFlyweight();
            flyweights.put(key, fw);
            return fw;
        } 
    }
}

享元对象能作到共享的关键是区份内部状态(internal state)和外部状态(external state)。下面简单对享元的内部状态和外部状态进行分析:

  1. 内部状态是存储在享元对象内部而且不会随环境改变而改变的状态,所以内部状态能够共享。
  2. 外部状态是随环境改变而改变的、不可共享的状态。享元对象的外部状态必须由客户端保存,并在享元对象被建立以后,在须要使用时再传入到享元对象内部。

典型的享元类代码以下:

public class Flyweight {

    private String intrinsicState;

    public Flyweight(String intrinsicState) {
        this.intrinsicState = intrinsicState;
    }

    public void operation(String extrinsicState) {
        ...
    }
}

实例之共享网络设备

不少网络设备都是支持共享的,如交换机、集线器等,多台计算机终端能够连接同一台网络设备,并经过该网络设备进行数据转换。可是分配给每个终端计算机的端口是不一样的,能够将端口从网络设备中抽取出来做为外部状态,须要时再设置。

在这里插入图片描述

  1. 抽象享元类 NetworkDevices

    public interface NetworkDevice {
    
        public String getType();
        public void use(Port port);	// 用于设置外部状态
    }
  2. 具体享元类 Switch

    public class Switch implements NetworkDevice {
    
        private String type;
    
        public Switch(String type) {
            this.type = type;
        }
    
        public String getType() {
            return type;
        }
    
        public void use(Port port) {
            System.out.println("Linked by switch, type is" + this.type + ",port is" + port.getPort());
        }
    }
  3. 具体享元类 Hub

    public class Hub implements NetworkDevice {
    
        private String type;
    
        public Hub(String type) {
    
            this.type = type;
        }
    
        public String getType() {
            return type;
        }
    
        public void use(Port port) {
            System.out.println("Linked by Hub, type is" + this.type + ",port is" + port.getPort());
        }
    }
  4. 端口类 Port

    public class Port {
    
        String port;
    
        public Port(String port) {
            this.port = port;
        }
    
        public String getPort() {
            return port;
        }
    
        public void setPort(String port) {
            this.port = port;
        }
    }
  5. 享元工厂 DeviceFactory

    public class DeviceFactory {
    
        private List<NetworkDevice> devices = new ArrayList<NetworkDevice>();
        private int totalTerminal = 0;
    
        public DeviceFactory() {
    
            NetworkDevice nd1 = new Switch("Cisco-WS-C2950-24");
            devices.add(nd1);
            NetworkDevice nd2 = new Hub("TP-LINK-HF8M");
            devices.add(nd2);
        }
    
        public NetworkDevice getNetworkDevice(String type) {
    
            if (type.equalsIgnoreCase("cisco")) {
                totalTerminal++;
                return devices.get(0);
            } else if (type.equalsIgnoreCase("tp")) {
                totalTerminal++;
                return devices.get(1);
            } else {
                return null;
            }
        }
    
        public int getTotalDevice() {
            return devices.size();
        }
    
        public int getTotalTerminal() {
            return totalTerminal;
        }
    }
  6. 客户测试类 Client

    public class Client {
    
        public static void main(String[] args) {
    
            DeviceFactory df = new DeviceFactory();
    
            NetworkDevice nd1 = df.getNetworkDevice("cisco");
            nd1.use(new Port("1000"));
    
            NetworkDevice nd2 = df.getNetworkDevice("cisco");
            nd2.use(new Port("1001"));
    
            NetworkDevice nd3 = df.getNetworkDevice("cisco");
            nd3.use(new Port("1002"));
    
            NetworkDevice nd4 = df.getNetworkDevice("tp");
            nd4.use(new Port("1003"));
    
            NetworkDevice nd5 = df.getNetworkDevice("tp");
            nd5.use(new Port("1004"));
    
            System.out.println("Total Device: " + df.getTotalDevice());
            System.out.println("Total Terminal: " + df.getTotalTerminal());
        }
    }

在客户端代码中,在调用享元对象的 use() 方法时,传入了一个 Port 类型对象,在该对象中封装了端口号,做为共享网络设备的外部状态,同一个网络设备具备多个不一样的端口号。

在这里插入图片描述

从运行结果能够得知,在调用享元对象的 use() 方法时,因为设置了不一样的端口号,所以相同的享元对象虽然具备相同的内部状态 type,可是它们的外部状态 port 不一样。


模式优缺点

优势以下:

  1. 极大减小内存中对象的数量
  2. 享元模式的外部状态相对独立,不会影响其内部状态,所以享元对象能够在不一样的环境中被共享。

缺点以下:

  1. 享元模式是系统更加复杂,须要分离出内部状态和外部状态。
  2. 读取外部状态会使运行时间变长。

模式适用环境

如下状况可使用享元模式:

  1. 一个系统有大量相同或类似对象,这类对象的大量使用形成内存的大量耗费
  2. 对象的大部分状态均可外部化,能够将这些外部状态传入对象中。
  3. 维护享元池须要耗费资源,所以应当在屡次重复使用享元对象时才值得使用享元模式

单纯享元模式

即全部抽象享元类的子类均可以共享,不存在非共享具体享元类
在这里插入图片描述


复合享元模式

将单纯享元模式与组合模式加以组合,能够造成复合享元对象。这样的复合享元对象自己不能共享,但它们能够分解成单纯享元对象,然后者能够共享。经过复合享元模式,能够确保复合享元类 CompositeConcreteFlyweight 中所包含的每一个单纯享元类 ConcreteFlyweight 都具备相同的外部状态,而这些单纯享元的内部状态每每不一样。
在这里插入图片描述

相关文章
相关标签/搜索