1. Factory(工厂)
1 简单工厂(Simple Factory)
根据参数的不同返回不同类的实例。
在创建一个对象时不向客户暴露内部细节,并提供一个创建对象的通用接口。
简单工厂把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个具体子类来实例化。
这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类。客户类往往有多个,如果不使用简单工厂,那么所有的客户类都要知道所有子类的细节。而且一旦子类发生改变,例如增加子类,那么所有的客户类都要进行修改。
模式应用
java.text.DateFormat
public final static DateFormat getDateInstance(int style);
2 工厂方法(Factory Method)
不传参数,而是分别建立工厂,使用工厂方法
模式应用
JDBC 中的工厂方法
Connection conn = DriverManager.getConnection("jdbc:……");
Statement statement = conn.createStatement();
3 抽象工厂(Abstract Factory)
系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
模式应用
在很多软件系统中需要更换界面主题,要求界面中的按钮、文本框、背景色等一起发生改变。
3.1 增加工厂
- 新增一个工厂子类
- 并增加相应产品子类
3.2 增加产品
- 新增一个产品父类
- 再增加对应每个工厂的产品子类
- 最后在工厂父类、子类中新增产品方法
2. Builder(建造者)
使用多个简单的对象一步一步构建成一个复杂的对象。
工厂模式关注的是创建单个产品,而建造者模式则关注创建符合对象,多个部分
MealBuilder mealBuilder = new MealBuilder();
Meal vegMeal = mealBuilder.prepareVegMeal();
Meal nonVegMeal = mealBuilder.prepareNonVegMeal();
模式应用
- StringBuilder
- 游戏软件中,地图包括天空、地面、背景等组成部分,人物角色包括人体、服装、装备等组成部分,可以使用建造者模式对其进行设计,通过不同的具体建造者创建不同类型的地图或人物。
3. Prototype(原型)
当直接创建对象的代价比较大时,创建当前对象的克隆
原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。
//原型角色:定义用于复制现有实例来生成新实例的方法;
// 1.(抽象类或者接口)实现 java.lang.Cloneable 接口
// 2.定义复制现有实例来生成新实例的方法
public abstract class Shape implements Cloneable {
public Object clone() {
}
}
//具体原型角色:实现用于复制现有实例来生成新实例的方法
// 1.实现复制现有实例来生成新实例的方法(也可以由超类完成)
public class Rectangle extends Shape {
}
//使用者角色:维护一个注册表,并提供一个找出正确实例原型的方法。最后,提供一个获取新实例的方法,用来委托复制实例的方法生成新实例。
public class ShapeCache {
//维护一个注册表
private static Hashtable<String, Shape> shapeMap
= new Hashtable<String, Shape>();
public static Shape getShape(String shapeId) {
Shape cachedShape = shapeMap.get(shapeId);
return (Shape) cachedShape.clone();
}
public static void loadCache() {
Circle circle = new Circle();
circle.setId("1");
shapeMap.put(circle.getId(),circle);
}
}
模式应用
一个对象需要在一个高代价的数据库操作之后被创建,可以缓存该对象,在下一个请求时返回它的克隆
4. Singleton(单例)
单例类只有一个实例,而且自行实例化并向整个系统提供这个实例
构造函数私有
模式应用
- 要求生产唯一序列号。
- WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
- 创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
几种实现方式
建议使用饿汉方式。只有在要明确实现懒加载效果时,才使用静态内部类方式。如果涉及到反序列化创建对象时,可以尝试使用枚举方式。如果有其他特殊的需求,可以考虑使用双检锁方式。
饿汉式
类加载时就初始化
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
静态内部类
延迟加载,类加载线程互斥
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
枚举
支持序列化,最佳方法
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
双重校验锁
singleton = new Singleton();
可能被优化为先分配空白空间给singleton
,再初始化。所以synchronized
里有可能singleton == null
为true,但还未初始化,会出错。
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}