目录
3、面向代码调优的设计模式
本质:减少对象的创建,尽量使用已经创建的对象
3.1、singleton(单例模式)
强制client只能创建一个object,避免因为new操作所带来的时空性能损失,也便于复用。
举例
public class Singleton {
private static final Singleton instance = null;
private Singleton() {…}
public static Singleton getInstance() {
/* 通过判断,只创建一个实例 */
if (instance == null)
instance = new Singleton();
return instance;
}
// other operations and data
}
关于单例模式的判断和代理模式的例子,有类似之处,都是判断如果已经有了,则不再创建,直接返回,降低代价和空间。
Recall
在state
模式中,两个状态是static
的,当返回新状态时,返回Statet2.instance
或State1.instance
,保证始终只有两个状态实例。
3.2、prototype(原型模式)
通过克隆而非new来创建object。
继承树
举例
/* 创建实现Cloneable接口的抽象类,作为原型 */
public abstract class Shape implements Cloneable {
private String id;
protected String type;
abstract void draw();
public String getType(){ return type; }
public String getId() { return id; }
public void setId(String id) { this.id = id;}
/* 实现clone方法 */
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
/* 继承原型类 */
public class Rectangle extends Shape {
public Rectangle(){
type = "Rectangle";
}
@Override
public void draw() {
...
}
}
/* 继承原型类 */
public class Square extends Shape {
public Square(){
type = "Square";
}
@Override
public void draw() {
...
}
}
/* 客户端 */
Shape rectangle = new Rectangle();
Shape square = new Square();
/* 使用克隆 */
Shape clonedShape1 = (Shape) rectangle.clone();
Shape clonedShape2 = (Shape) square.clone();
分析,首先要实现一个原型抽象类,该抽象类实现Cloneable
接口,重写Object clone()
方法;然后两个继承原型的类,rectangle和square;客户端中创建分别创建两个原型子类的对象,之后再创建对象,直接使用对象的clone()
方法获得对象即可。
需要注意的是Cloneable
的clone
操作默认实现的是浅复制,对象内部的变量都是直接填充的,可能会出现内存泄露。
实现Cloneable
接口时,仅仅实现Cloneable
不够,还需要override
clone()
方法并将public
。因为Object.clone()
是protected
:它可以被同包(java.lang)下以及它
(java.lang.Object)的子类访问。自定义类无法直接使用Object.clone()
:没有访问权限(invisible),故需要override
。
3.3、flyweight(轻量模式)
该模式允许在应用中不同部分共享使用objects,降低大量objects带来的时空代价。
内部状态和外部状态,
内部特征:不管在什么场合使用该object,内部特征都不变;
外部特征:不是固定的,需要在不同场合分别计算并产生变化。
继承树
举例
/* 外特征 */
public enum Color {Red, Green, Blank, Blue, Yellow}
/* 可共享对象的抽象接口 */
public interface IAlien {
String Shape = null; //内部特征
String getShape();
Color getColor(int madLevel); //外部特征
}
/* 可共享对象类,Large Shape */
class LargeAlien implements IAlien{
private String shape = "Large Shape";
public String getShape() {
return shape;
}
/* 设置外部特征 */
public Color getColor(int madLevel) {
if (madLevel == 0)
return Color.Green;
else if (madLevel == 1)
return Color.Red;
else
return Color.Blue;
}
}
/* 可共享对象,Little Shape */
class LittleAlien implements IAlien {
private String shape = "Little Shape";
public String getShape() {
return shape;
}
public Color getColor(int madLevel) {
if (madLevel == 0)
return Color.Red;
else if (madLevel == 1)
return Color.Blue;
else
return Color.Green;
}
}
/* 工厂,可共享对象的索引,维护所有对象,根据client请求返回相应的对象 */
public class AlienFactory {
private Map<String, IAlien> list = new HashMap<>();
public void SaveAlien(String index, IAlien alien) {
list.put(index,alien);
}
public IAlien GetAlien(String index) {
return list.get(index);
}
}
/* 客户端 */
/* 工厂方法,保存两个对象 */
AlienFactory factory = new AlienFactory();
factory.SaveAlien("LargeAlien", new LargeAlien());
factory.SaveAlien("LittleAlien", new LittleAlien());
/* 根据索引从工厂取出对象 */
IAlien a = factory.GetAlien("LargeAlien");
IAlien b = factory.GetAlien("LittleAlien");
/* 内部特征 */
System.out.println("Showing intrinsic states...");
System.out.println("Alien of type LargeAlien is " + a.getShape());
System.out.println("Alien of type LittleAlien is " + b.getShape());
/* 外部特征 */
System.out.println("Showing extrinsic states...");
System.out.println("Alien of type LargeAlien is " + a.getColor(0).toString());
System.out.println("Alien of type LargeAlien is " + a.getColor(1).toString());
System.out.println("Alien of type LittleAlien is " + b.getColor(0).toString());
System.out.println("Alien of type LittleAlien is " + b.getColor(1).toString());
总体来看,有一个共享对象接口,以及两个实现接口的类,包括内部特征(已经确定)和外部特征(根据传入参数,确定特征);一个工厂方法,保存所有的共享对象,根据传入索引返回共享对象;客户端调用时,可以传入参数,选择不同的外在特征,使得一个共享对象可以表现出不同的外在特征,而内在特征相同。
3.4、object pool(对象池)
很多时候,object不用了就直接扔掉,需要时再new一个新object。对象复用:不要扔掉object,留着后续复用。
代价:原本可被GC的对象,现在要留在pool中,导致内存浪费——用空间换时间。
继承树
举例
//vector pool,管理所有的实例对象
public static VectorPoolMgr vectorPoolMgr = new VectorPoolMgr(25);
...
public void someMethod( ) {
//Get a new Vector. We only use the vector to do some stuff
//within this method, and then we dump the vector (i.e., it
//is not returned or assigned to a state variable)
//so this is a perfect candidate for reusing Vectors.
//Use a factory method instead of 'new Vector( )'
/* 从vector pool中取出一个对象,而不是使用new */
Vector v = vectorPoolMgr.getVector();
/* 对对象的属性进行修改 */
... //do vector manipulation stuff
//and the extra work is that we have to explicitly tell the
//pool manager that we have finished with the vector
/* 返回对象 */
vectorPoolMgr.returnVector(v);
}
类似一个工厂方法,对象池管理所有废弃的对象,当需要一个新的对象时,从对象池中出去即可,取出后可以对属性进行修改配置,然后返回。