设计模式(四):代码调优的设计模式

1.    概述:

    本文将介绍四种面向代码调优的设计模式:

    (1)Singleton Pattern(单例模式)

    (2)Flyweight Pattern(轻量模式)

    (3)Prototype Pattern(原型模式)

    (4)Object Pool Pattern(对象池模式)


2.    Singleton Pattern(单例模式)

    某些类在应用运行期间只需要一个实例

    可强制client只能创建一个object实例,避免因为new操作所带来的时空性能

    (尤其是GC)的损失,也便于复用。

    模式介绍:

    (1)设置静态变量来存储单一实例对象

    (2)将构造器设置为private,从而client无法new

    (3)从构造器中new新实例

    (4)提供静态方法来获取单一实例对象

    在java实现中:

    在类中定义一个final static属性,为该类的实例

    使用public static 方法getInstance()获得该实例。

    设计模式(四):代码调优的设计模式

    还有一种实现:

    进一步提升性能:在需要的时候再new,而非提前构造出来

    设计模式(四):代码调优的设计模式

    回顾我们基于状态的设计模式:state模式,其中就使用了singleton模式:

    设计模式(四):代码调优的设计模式


3.    Flyweight Pattern(轻量模式)

考虑文本编辑器中的“字符”,同一个字符重复出现多次,代表同样内容,但字体字号等不同 ,这时我们考虑一种新的轻量模式。

该模式允许在应用中不同部分共享使用objects,降低大量objects带来的时空代价。

使用该模式的类:分为内部状态和外部状态:

内部特征:不管在什么场合使用该object,内部特征都不变

外部特征:不是固定的,需要在不同场合context分别指派/计算其值 

模式示意图:

设计模式(四):代码调优的设计模式

一个实例:

游戏中的玩偶,有很多个,每个玩偶具有某些固有属性(Shape),但玩偶在不同的场景下颜色不同(Color)。无需为每个玩偶new一个object,可以复用。

定义外特征颜色:

设计模式(四):代码调优的设计模式

再定义一个可共享对象的抽象接口:IAlien

设计模式(四):代码调优的设计模式

shape属于内部状态,是不能改变的。

在其实现中,实现两种类型:LargeAlien和LittleAlien,其内特征shape不同。

getShape返回内特征,getColor返回外特征:

设计模式(四):代码调优的设计模式

再定义一个工厂类:AlienFactory

设计模式(四):代码调优的设计模式

该工厂相当于可共享对象的索引,维护所有对象并根据client请求返回相应内特征的对象。

客户端使用:

设计模式(四):代码调优的设计模式

Flyweight对比Singleton:

单例Singleton:不区分各场合下的不同表现形式,统一用一个实例表示。 

轻量对象:同一个事物,具有多种不同表现形式 (外部状态)

    外特性并非其属性,而是根据输入返回不同的值,因此轻量对象仍是immutable的

而单例对象通常是mutable的,经常在多线程中被使用。


4.    Prototype Pattern(原型模式)

通过克隆而非new来创建object:

直接new的时空代价高,尤其 是需要与外部I/O、网络、数据库打交道时候。例如ConcreteGraph…

其模式结构图如下:

设计模式(四):代码调优的设计模式

其中抽象类Prototype实现Cloneable接口,重写其Clone方法。

一个实例:Shape(Prototype)

设计模式(四):代码调优的设计模式

注意,需要将其public化。

再扩展出两个子类:

设计模式(四):代码调优的设计模式

就可以在客户端调用:

设计模式(四):代码调优的设计模式

这里介绍两个概念:

引用拷贝和对象拷贝

引用拷贝:两个引用变量指向同一个对象实例

对象拷贝:创建一个新的实例,拥有相同的属性。

设计模式(四):代码调优的设计模式

设计模式(四):代码调优的设计模式

浅拷贝与深拷贝:

浅拷贝:使用一个已知实例的成员变量对新创建实例的成员变量逐个赋值。只复制对象本身,但不复制对象对外的引用 

深拷贝:类的拷贝方法不仅要复制对象的所有非引用成员变量值(简单 数据类型),还要为引用类型(对象)的成员变量创建新的实例,并且初始化为原对象的值。

浅拷贝很容易造成表示泄露,因为新创建实例的成员变量,其内部引用的对象仍为原来的对象,对其修改会造成原来值的变化。

Deep Copy:

设计模式(四):代码调优的设计模式

object中自带的clone方法:进行的是浅拷贝,其契约:

设计模式(四):代码调优的设计模式

因此重写的clone()要保证新对象与原对象要相互独立,要求deep copy 

因此重写时,如果内部只有简单数据类型的变量或 immutable的变量,则直接调用父类缺省的clone函数即可。

在让你的ADT支持Cloneable接口的时候,千万注意自己override的 clone()正确的实现了deep copy。

5.    Object Pool Pattern(原型模式)

很多时候,object 不用了就直接扔掉,需要时再new一个新object,付出了巨大性能代价。

我们可进行这样的对象复用:不要扔掉object,留着后续复用。

建立一个Object Pool,像一个图书馆,“书” 就是可复用的object,可借可还。

模式结构图:

设计模式(四):代码调优的设计模式

付出的代价::原本可被GC的对象,现在要留在pool中,导致内存浪费—— 用空间换时间

注意:Singleton和Flyweight本质上都是object pool,但有不同的变化