这里我们主要讨论 结构型模式
适配器模式,:
● Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。
● Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。
● Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
注意上图的 Adapter、Adaptee 是关联关系,实际上,我们也可以做成 继承关系,注意,因为java不支持多继承,所以这个时候,Adapter和Target 的关系必须不能是 继承了,一般就是 实现接口的关系。
上面两种都是单向适配,就是说,只能通过 Target 访问 Adaptee 的功能, 而不能反过来。 实际上,我们有时候可能需要双向适配:
还有 缺省适配器:
适配器模式, 简单说就是 将没有继承实现关系的 但又有些近似、相同的两个功能,通过一个adapter整合到一起。
适用范围:将没有继承实现关系的 但又有些近似、相同的两个功能,通过一个adapter整合到一起。 (当然,如果 差别很大,那也没什么必要非要适配到一起了)
优缺点:
由于java不支持多继承,所以我们的adapter不能适配多个适配者(这个其实也不是什么大问题)
适配者类不能为最终类,如在Java中不能为final类
类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性。
讨论:
Target怎么理解,就是我们需要去适配的目标!client 通过 Target 来访问,而Adaptee 是 对Target 的适配,Adaptee 对象适配器模式中,不能是接口,必须是具体类。Adapter 显然必须是 具体类。
实际情况,往往复杂而且难以捉摸。 我们设计模式讨论的是最简单的 类的结构、行为。那么,Target 可以存在其他需要适配的接口吗? 当然,也是可以有的,这个时候呢,我们需要一个Adapter + 多个Adaptee ,Adapter 实现了多个适配方法。
拓展性:Target和Adapter 之间存在继承关系,所以我们容易拓展多个 Adapter 。 但是实际上,我们真的需要吗? 不好说
代理模式:
(1) Subject(抽象主题角色):它声明了真实主题和代理主题的共同接口,这样一来在任何使用真实主题的地方都可以使用代理主题,客户端通常需要针对抽象主题角色进行编程。
(2) Proxy(代理主题角色):它包含了对真实主题的引用,从而可以在任何时候操作真实主题对象;在代理主题角色中提供一个与真实主题角色相同的接口,以便在任何时候都可以替代真实主题;代理主题角色还可以控制对真实主题的使用,负责在需要的时候创建和删除真实主题对象,并对真实主题对象的使用加以约束。通常,在代理主题角色中,客户端在调用所引用的真实主题操作之前或之后还需要执行其他操作,而不仅仅是单纯调用真实主题对象中的操作。
(3) RealSubject(真实主题角色):它定义了代理角色所代表的真实对象,在真实主题角色中实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的操作。
拓展性:上面的图,我们可以看到, 存在两个 继承关系,那么 我们当然很容易对我们的主题进行各种代理,但是注意到 Proxy 存在一个RealSubject引用, 所以,Proxy、RealSubject 是成对出现的。
讨论:1 Subject 是必须的吗?我认为Subject、RealSubject可以合并到一起。 当然,复杂一些的情况,我们可能需要多个RealSubject,这样Subject 也是不可少的。
2 Proxy 可以代理 多个RealSubject吗?不太好,虽然我们可以给Proxy 多个realSubject 引用,比如realSubject1, realSubject2 分别对应两个真实的 主题,但是我们只有一个 request 方法,不方便一个request方法内部 代理 处理两个 主题。
3 RealSubject可以 使用多个 Proxy 代理吗? 完全没问题。
4 我们可以给 Subject 设置多个方法吗? 比如 request1,request2,表示 即代理这个事情,又那个事情。 理论上是可以的。但是毕竟麻烦,不如使用多个Subject更好?
5 如果我想代理的RealSubject没有实现某接口,换句话说,不存在 Subject (这也是常常 看到的 实际情况), 怎么做代理? 那我们可以省去 Subject, 或者采取其他的一些代理方式。
代理 这个词的使用场景非常非常多,其语义范畴也是十分的广泛。
分类: 虚拟~、远程~、保护~、cache~、防火墙~、同步化~、智能引用~、copy-on-write~
在实际开发过程中,代理类的实现比上述代码要复杂很多,代理模式根据其目的和实现方式不同可分为很多种类,其中常用的几种代理模式简要说明如下:
(1) 远程代理(Remote Proxy):为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中,远程代理又称为大使(Ambassador)。
(2) 虚拟代理(Virtual Proxy):如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。
(3) 保护代理(Protect Proxy):控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
(4) 缓冲代理(Cache Proxy):为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
(5) 智能引用代理(Smart Reference Proxy):当一个对象被引用时,提供一些额外的操作,例如将对象被调用的次数记录下来等。
在这些常用的代理模式中,有些代理类的设计非常复杂,例如远程代理类,它封装了底层网络通信和对远程对象的调用,其实现较为复杂。。
讨论:
代理模式是容易理解的,也就 两个继承关系+ 一个关联关系而已吧。 但是按照场景(按代理类的preRequest、postRequest 的具体工作的不同)细分下来,又变得复杂起来了,这么多分类,也很难记住,有时候也不是叫做这个名称,不必花任何时间去记忆, 记住了就记住了,记不住就算了。
装饰模式:
● Component(抽象构件):它是具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。
● ConcreteComponent(具体构件):它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法)。
● Decorator(抽象装饰类):它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。
● ConcreteDecorator(具体装饰类):它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。
由于具体构件类和装饰类都实现了相同的抽象构件接口,因此装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。
上面的uml 有些复杂。装饰模式的核心在于抽象装饰类的设计
可以多次的装饰,就是嵌套,或者说wrapper,从而实现更加复杂的功能。java io 的Filter流,可以 动态 增减 功能。
在实际使用过程中,由于新增行为可能需要单独调用,因此这种形式的装饰模式也经常出现,这种装饰模式被称为半透明(Semi-transparent)装饰模式,而标准的装饰模式是透明(Transparent)装饰模式。
(1) 尽量保持装饰类的接口与被装饰类的接口相同,这样,对于客户端而言,无论是装饰之前的对象还是装饰之后的对象都可以一致对待。这也就是说,在可能的情况下,我们应该尽量使用透明装饰模式。
(2) 尽量保持具体构件类ConcreteComponent是一个“轻”类,也就是说不要把太多的行为放在具体构件类中,我们可以通过装饰类对其进行扩展。
(3) 如果只有一个具体构件类,那么抽象装饰类可以作为该具体构件类的直接子类。
简化:---------------------Component 是否可以是 接口? 我认为 理论上也是可以的,但是,接口 一般不宜继续做 封装,故装饰模式不适合接口。
装饰模式的关键就是, 我们需要 Decarator 继承 Component(或 ConcreteComponent)
适用场景:在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
拓展性: 虽然存在3个实现关系,但是,关键的恐怕还是 ConcreteDecorator 对 Decorator 的实现关系。
讨论:
1 Decorator 是可以省去的吗? 我认为完全可以省去,但是如果需要多个 ConcreteComponent, 那么Decorator 自然不能去掉。
2 我们看到了,装饰模式很重要一点是需要继承Component,那么它和继承有什么大的区别,ConcreteDecorator 直接继承Component或 ConcreteComponent是不是就好了?为什么要多此一举? 直接继承自然是可以 进行 功能拓展的,但是, 如果 需要灵活的 添加各种功能, 那么 继承类的 个数会 急剧上升。 而使用 Decorator 模式的 优点也正式如此:
(1) 对于扩展一个对象的功能,装饰模式比继承更加灵活性,不会导致类的个数急剧增加。
(2) 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的具体装饰类,从而实现不同的行为。
(3) 可以对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合,得到功能更为强大的对象。
(4) 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,原有类库代码无须改变,符合“开闭原则”。
---------------------—— 特别需要注意的是 Decorator 不仅仅是继承于 Component,而且它还聚合了一个 Component,Decorator 的主要功能仍然是Component 提供的,它只不过是 做了一些装饰,这也是 装饰模式 名称的 由来吧~!
关于为什么“装饰模式比继承更加灵活性,不会导致类的个数急剧增加” , 我们看看java io 的FilterStream 就知道了, 如果不这样做,那么各种流,不知道会有多少, 而且 用起来也是非常不方便,需要搞懂每个流的细微区别才行。
装饰模式降低了系统的耦合度,可以动态增加或删除对象的职责,并使得需要装饰的具体构件类和具体装饰类可以独立变化,以便增加新的具体构件类和具体装饰类。在软件开发中,装饰模式应用较为广泛,例如在JavaIO中的输入流和输出流的设计、javax.swing包中一些图形界面构件功能的增强等地方都运用了装饰模式
---------------------讨论2:
装饰模式 和 代理模式 非常相似,有一些细微的差别:
1 装饰模式的 ConcreteDecorator 的 component 字段 聚合Component,而不是ConcreteComponent, 这意味着 component 字段 可以是继续是一个ConcreteDecorator ; Proxy 有一个 realSubject 字段 关联指向 RealSubject 而不是 Subject, 意味着不能在嵌套了,这是语法上的限制也是 语义上的 需求。 这一点就意味着 装饰模式 更加灵活。
2 忽略字面含义,Subject 和 Component的 语义也是差别很大的。实际的Subject 可能不存在, 而 Component通常是存在的。
3 装饰模式 倾向于 “动态的添加功能” (不会动态减少功能,不需要那么不装饰就是了,没必要减少人家原本有的功能。。),前也好,后也好。。, 代理模式 常用于 在 RealSubject 实际工作的前后做一些 “不是很紧要的工作”
外观模式:
为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
(1) Facade(外观角色):在客户端可以调用它的方法,在外观角色中可以知道相关的(一个或者多个)子系统的功能和责任;在正常情况下,它将所有从客户端发来的请求委派到相应的子系统去,传递给相应的子系统对象处理。
(2) SubSystem(子系统角色):在软件系统中可以有一个或者多个子系统角色,每一个子系统可以不是一个单独的类,而是一个类的集合,它实现子系统的功能;每一个子系统都可以被客户端直接调用,或者被外观角色调用,它处理由外观类传过来的请求;子系统并不知道外观的存在,对于子系统而言,外观角色仅仅是另外一个客户端而已。
---------------------在标准的外观模式结构图中,如果需要增加、删除或更换与外观类交互的子系统类,必须修改外观类或客户端的源代码,这将违背开闭原则,因此可以通过引入抽象外观类来对系统进行改进,在一定程度上可以解决该问题。—— 我认为非要这么做,这个模式就不是 Facade 模式了!
如绝大多数B/S系统都有一个首页或者导航页面,大部分C/S系统都提供了菜单或者工具栏,在这里,首页和导航页面就是B/S系统的外观角色,而菜单和工具栏就是C/S系统的外观角色,通过它们用户可以快速访问子系统,降低了系统的复杂程度。所有涉及到与多个业务对象交互的场景都可以考虑使用外观模式进行重构。
---------------------拓展性:这个模式 是谈不上拓展性的,一拓展子系统就 违背了 开闭原则。
讨论:
这个模式, 其实不算是真正的模式, 只能说是一个思想。
常见场景:B/S系统首页、C/S系统的菜单或者工具栏 ...
桥接模式:
桥接模式(Bridge Pattern) 将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。
●Abstraction(抽象类):用于定义抽象类的接口,它一般是抽象类而不是接口,其中定义了一个Implementor(实现类接口)类型的对象并可以维护该对象,它与Implementor之间具有关联关系,它既可以包含抽象业务方法,也可以包含具体业务方法。
●RefinedAbstraction(扩充抽象类):扩充由Abstraction定义的接口,通常情况下它不再是抽象类而是具体类,它实现了在Abstraction中声明的抽象业务方法,在RefinedAbstraction中可以调用在Implementor中定义的业务方法。
●Implementor(实现类接口):定义实现类的接口 (是个 接口!),这个接口不一定要与Abstraction的接口完全一致,事实上这两个接口可以完全不同,一般而言,Implementor接口仅提供基本操作,而Abstraction定义的接口可能会做更多更复杂的操作。Implementor接口对这些基本操作进行了声明,而具体实现交给其子类。通过关联关系,在Abstraction中不仅拥有自己的方法,还可以调用到Implementor中定义的方法,使用关联关系来替代继承关系。
●ConcreteImplementor(具体实现类):具体实现Implementor接口,在不同的ConcreteImplementor中提供基本操作的不同实现,在程序运行时,ConcreteImplementor对象将替换其父类对象,提供给抽象类具体的业务操作方法。
这个模式有些难以理解,Abstraction,Implementor 的语义不太好,模式中 什么是抽象部分,什么是实现部分? 有明确的定义吗?没有吧,我觉得 不一定非要是 抽象和实现,Abstraction理解成 静态的,Implementor 理解为 动态变化的部分是不是更好? 理解成 两个维度是不是更好?
(我认为, 上图的 operation() 和 operationImpl() 其实可以没有任何关系的,这样的命名完全就是 误导。 不需要,也不应该这样命名。)
桥接模式总结
桥接模式是设计Java虚拟机和实现JDBC等驱动程序的核心模式之一,应用较为广泛。在软件开发中如果一个类或一个系统有多个变化维度时,都可以尝试使用桥接模式对其进行设计。桥接模式为多维度变化的系统提供了一套完整的解决方案,并且降低了系统的复杂度。1.主要优点
桥接模式的主要优点如下:
(1)分离抽象接口及其实现部分。桥接模式使用“对象间的关联关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。所谓抽象和实现沿着各自维度的变化,也就是说抽象和实现不再在同一个继承层次结构中,而是“子类化”它们,使它们各自都具有自己的子类,以便任何组合子类,从而获得多维度组合对象。
(2)在很多情况下,桥接模式可以取代多层继承方案,多层继承方案违背了“单一职责原则”,复用性较差,且类的个数非常多,桥接模式是比多层继承方案更好的解决方法,它极大减少了子类的个数。
(3)桥接模式提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统,符合“开闭原则”。
2.主要缺点
桥接模式的主要缺点如下:
(1)桥接模式的使用会增加系统的理解与设计难度,由于关联关系建立在抽象层,要求开发者一开始就针对抽象层进行设计与编程。
(2)桥接模式 要求 正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性,如何正确识别两个独立维度也需要一定的经验积累。
---------------------适用场景
在以下情况下可以考虑使用桥接模式:
(1)如果一个系统需要在抽象化和具体化之间增加更多的灵活性,避免在两个层次之间建立静态的继承关系,通过桥接模式可以使它们在抽象层建立一个关联关系。
(2) “抽象部分”和“实现部分”可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。
(3)一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展。
(4)对于那些不希望使用继承或因为多层继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
---------------------如果系统中存在两个以上的变化维度,是否可以使用桥接模式进行处理?如果可以,系统该如何设计? 我想自然是可以的,通过另外一个 impl 聚合另外一个Implementor2即可
适配器模式与桥接模式的联用
桥接的重点是 桥, 这里的桥,其实就是一个 聚合关系。通过一座桥 就为原来的Abstraction 提供了一个Implementor。 一个Implementor 就是一个额外的维度,增加更加多的桥,我们实现了更多的维度的拓展。而且, 需要特别注意的是, 这里的Implementor 就是一个额外的维度。是用加法实现乘法的拓展关系。 比如现在是3个维度,如果通过继承, 我们需要 3*3 个类,如果是 桥接,那么是 3+3。—— 从这点来看,桥接和 装饰模式 有异曲同工之妙。
桥接和 装饰模式 的区别呢?
桥接模式 是通过 一个 聚合关系 拓展了 一个 维度。
装饰模式 是通过继承+聚合,在原来各个方法上 增加了一些功能,主要的功能, 是没有太多变化的!
简化: 如果只有一个ConcreteImplementor的话,似乎可以去掉Implementor了,同理Abstraction。
拓展性:没有装饰模式的“继承”约束,这个模式是很自由的,所以拓展性相当好。
讨论:
Implementor 是否可以存在多个方法, 比如 operationImpl1 operationImpl2? 我认为完全是可以的,具体场景具体分析。 然而,如果Implementor 存在多个方法,那么 把Implementor 拆分为多个接口,变成多个维度,是不是更好?
组合模式:
组合模式(Composite Pattern) 组合多个对象形成树形结构以表示具有“整体—部分”关系的层次结构。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性,组合模式又可以称为“整体—部分”(Part-Whole)模式,它是一种对象结构型模式。
● Component(抽象构件):它可以是接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类共有行为的声明和实现。在抽象构件中定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等。
● Leaf(叶子构件):它在组合结构中表示叶子节点对象,叶子节点没有子节点,它实现了在抽象构件中定义的行为。对于那些访问及管理子构件的方法,可以通过异常等方式进行处理。
● Composite(容器构件):它在组合结构中表示容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子节点的业务方法。
---------------------组合模式的关键是定义了一个抽象构件类,它既可以代表叶子,又可以代表容器,而客户端针对该抽象构件类进行编程,无须知道它到底表示的是叶子还是容器,可以对其进行统一处理。同时容器对象与抽象构件类之间还建立一个聚合关联关系,在容器对象中既可以包含叶子,也可以包含容器,以此实现递归组合,形成一个树形结构。
如果不使用组合模式,客户端代码将过多地依赖于容器对象复杂的内部实现结构,容器对象内部实现结构的变化将引起客户代码的频繁变化,带来了代码维护复杂、可扩展性差等弊端。组合模式的引入将在一定程度上解决这些问题。
---------------------关键其实就在于 组件容器的引入,使得存在一种灵活的“包含”的关系。 组合模式的 容器, 用到了 迭代器模式。
组合模式做一些变化,然后分类,可分为 透明组合模式与安全组合模式:
透明组合模式:
安全组合模式:
适用场景
在以下情况下可以考虑使用组合模式:
(1) 在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致地对待它们。
(2) 在一个使用面向对象语言开发的系统中需要处理 一个树形结构。
(3) 在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新的类型。
---------------------Composite 和 Leaf 并没有功能上的很大的区别,不过是Composite 是Leaf 的集合,提供了集合操作的功能而已。
拓展性:uml存在两个实现,那么,新增Leaf 和 Composite 都是很方便的。
讨论,组合模式关键就在于 Composite 的出现,使得它区别于一般的继承,Composite 存在一个 集合 聚合了Component 本身,同时又提供了其他和Component 一模一样的功能。所以,我们使用此模式的时候,关键就是说 是否存在这么一个 Composite,组合Leaf, 统一的对外提供服务 。
简化:除去client还有3个关系,都不能去掉。所以,显然已经无法再简化了!
享元模式:
在享元模式结构图中包含如下几个角色:
● Flyweight(抽象享元类):通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。
● ConcreteFlyweight(具体享元类):它实现了抽象享元类,其实例称为享元对象;在具体享元类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
● UnsharedConcreteFlyweight(非共享具体享元类):并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。
● FlyweightFactory(享元工厂类):享元工厂类用于创建并管理享元对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中,享元池一般设计为一个存储“键值对”的集合(也可以是其他类型的集合),可以结合工厂模式进行设计;当用户请求一个具体享元对象时,享元工厂提供一个存储在享元池中已创建的实例或者创建一个新的实例(如果不存在的话),返回新创建的实例并将其存储在享元池中。
---------------------复合享元模式
将一些单纯享元对象使用组合模式加以组合,还可以形成复合享元对象,这样的复合享元对象本身不能共享,但是它们可以分解成单纯享元对象,而后者则可以共享
当系统中存在大量相同或者相似的对象时,享元模式是一种较好的解决方案,它通过共享技术实现相同或相似的细粒度对象的复用,从而节约了内存空间,提高了系统性能。相比其他结构型设计模式,享元模式的使用频率并不算太高,但是作为一种以“节约内存,提高性能”为出发点的设计模式,它在软件开发中还是得到了一定程度的应用。
1.主要优点
享元模式的主要优点如下:
(1) 可以极大减少内存中对象的数量,使得相同或相似对象在内存中只保存一份,从而可以节约系统资源,提高系统性能。
(2) 享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。
2.主要缺点
享元模式的主要缺点如下:
(1) 享元模式使得系统变得复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。
(2) 为了使对象可以共享,享元模式需要将享元对象的部分状态外部化,而读取外部状态将使得运行时间变长。
3.适用场景
在以下情况下可以考虑使用享元模式:
(1) 一个系统有大量相同或者相似的对象,造成内存的大量耗费。
(2) 对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。
(3) 在使用享元模式时需要维护一个存储享元对象的享元池,而这需要耗费一定的系统资源,因此,应当在需要多次重复使用享元对象时才值得使用享元模式。
---------------------元意味着 本质的 东西,更加细化的东西,拥有内部状态的东西。享元其实可以理解 为 一个 池子, 比如 数据库连接池、线程池,对象池。。。它其实更加简单,它是一个非常简单的池子,基本上就是 封装了一个 HashMap, 然后把 HashMap的 get set 方法 合二为一。
简化:只有3个关系,都不能减少或改变。
本文大部分参考:https://blog.csdn.net/lovelion