设计模式(四):代码调优的设计模式
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,但有不同的变化