写在前面
本篇文章通过代码演示的形式,记录创建型设计模式的相关学习总结,包括:单例模式、工厂模式(三种)、建造者模式
详细内容可参考 Java设计模式、图说设计模式
一、单例模式
单例模式确保某一个类只有一个实例,并提供全局访问方法供整个系统使用该实例(类构造方法设置为私有)。
单例模式主要有以下几种写法:
1、饿汉模式
/**
* 饿汉模式
*/
public class HungrySingleton {
private static HungrySingleton sInstance = new HungrySingleton();
private HungrySingleton() {}
public static HungrySingleton getInstance() {
return sInstance;
}
}
避免了线程安全问题,但浪费资源(不管用没用到都会创建实例)
2、懒汉模式
/**
* 懒汉模式
*/
public class LazySingleton {
private static LazySingleton sInstance;
private LazySingleton() {}
public static synchronized LazySingleton getInstance() {
if (sInstance == null) {
sInstance = new LazySingleton();
}
return sInstance;
}
}
时间换空间,用到时才创建,每次获取都判断实例是否已创建;通过synchronized关键字来保证线程安全,无该关键字的懒汉模式是线程不安全的
系统开销大,效率较低
3、双检锁
/**
* 双检锁
*/
public class DoubleCheck {
private static DoubleCheck sInstance;
private DoubleCheck() {}
public static DoubleCheck getInstance() {
if (sInstance == null) {
synchronized (DoubleCheck.class) {
if (sInstance == null) {
sInstance = new DoubleCheck();
}
}
}
return sInstance;
}
}
减少了使用锁时的系统开销,兼容性能及线程安全。JVM指令发生重排可能导致的问题这里不讨论,感兴趣的朋友请自行查询
4、静态内部类方法
/**
* 静态内部类模式
*/
public class StaticInner {
private static class SingletonHolder {
private static final StaticInner sInstance = new StaticInner();
}
private StaticInner() {}
public static StaticInner getInstance() {
return SingletonHolder.sInstance;
}
}
延迟加载不增加访问开销,同时保证线程安全
二、工厂模式
工厂模式,顾名思义就是创建一个工厂来生产产品,即实例化对象;对外封装产品细节,暴露接口供外部调用来创建实例。
1、简单工厂模式
先创建一些食物的实体类,实现Food接口,表示他们都属于Food类型
/**
* 构建实体类
*/
public interface Food {
void create();
}
public class Meat implements Food {
public Meat() {}
@Override
public void create() {
System.out.println("You got meat");
}
}
public class Vegetable implements Food {
public Vegetable() {}
@Override
public void create() {
System.out.println("You got vegetable");
}
}
public class Fruit implements Food {
public Fruit() {}
@Override
public void create() {
System.out.println("You got fruit");
}
}
再构造一个Food的工厂,来生产Food
/**
* 简单工厂模式
*/
public class SimpleFoodFactory {
public static Food createFood(String type) {
if ("meat".equals(type)) {
return new Meat();
} else if ("fruit".equals(type)) {
return new Fruit();
} else if ("vegetable".equals(type)) {
return new Vegetable();
}
return null;
}
}
这就是一个简单工厂模式的例子,通过告诉工厂需要什么样的实例类型,即可获得相应的实例对象
简单工厂模式的缺点是不方便维护,每次新增产品类型都要修改工厂类的方法,成本较高
2、工厂方法模式
依旧用上面的食物类型做例子,下面我们来看看工厂方法模式
/**
* 工厂类实现的接口
*/
public interface IFoodMethodFactory {
Food createFood();
}
public class MeatMethodFactory implements IFoodMethodFactory {
@Override
public Food createFood() {
return new Meat();
}
}
public class VegetablesMethodFactory implements IFoodMethodFactory {
@Override
public Food createFood() {
return new Vegetable();
}
}
public class FruitMethodFactory implements IFoodMethodFactory {
@Override
public Food createFood() {
return new Fruit();
}
}
与简单工厂模式不同的是,工厂方法模式给每个实体类型都创建了一个对应的工厂,每个工厂只生产一种产品,这样如果新增产品类型,只需要新增一个对应的工厂类即可,不必去修改原有的结构
这种模式的问题是,工程文件数和复杂度会随着实体类型的增加而不断增加
3、抽象工厂模式
再创建两个实体类
public class Apple extends Fruit {
public Apple() {}
}
public class Banana extends Fruit {
public Banana() {}
}
他们都是水果,我们还需要一个水果工厂来创建各种类型的水果
/**
* 抽象工厂模式
*/
public interface IAbstractFactroy {
Food createFoodA();
Food createFoodB();
}
/**
* 水果工厂,负责生产不同类型的水果
*/
public class FruitFactory implements IAbstractFactroy {
@Override
public Food createFoodA() {
return new Apple();
}
@Override
public Food createFoodB() {
return new Banana();
}
}
水果工厂生产不同类型的水果,我们还可以建造一个肉类工厂来生产各种肉食;每个工厂创建一系列有关联性的产品,这就是抽象工厂模式
三、建造者模式
建造者模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示,有两种写法,先来看基础的写法
/**
* 建造者模式:生产一辆车
*/
public interface Product {
}
public class Car implements Product {
private String mWheel;
private String mLights;
private String mSpeaker;
public void setWheel(String mWheel) {
this.mWheel = mWheel;
}
public void setLights(String mLights) {
this.mLights = mLights;
}
public void setSpeaker(String mSpeaker) {
this.mSpeaker = mSpeaker;
}
}
/**
* Builder接口
*/
public interface Builder {
void buildWheel();
void buildLights();
void buildSpeaker();
Product build();
}
/**
* 车的建造器
*/
public class CarBuilder implements Builder {
private Car mCar = new Car();
@Override
public void buildWheel() {
mCar.setWheel("four Wheels");
}
@Override
public void buildLights() {
mCar.setLights("two Lights");
}
@Override
public void buildSpeaker() {
mCar.setSpeaker("one Speaker");
}
public Product build() {
return mCar;
}
}
/**
* 车的组装类
*/
public class CarDirector {
Builder mBuild;
public CarDirector(Builder builder) {
this.mBuild = builder;
}
public Product construct() {
mBuild.buildLights();
mBuild.buildSpeaker();
mBuild.buildWheel();
return mBuild.build();
}
}
/**
* 买车的客户
*/
public class Client {
public static void main(String[] args) {
System.out.println("I need a car");
Builder builder = new CarBuilder();
CarDirector director = new CarDirector(builder);
Car car = (Car)director.construct();
System.out.println("I got my car " + car);
}
}
以上就是建造者模式的基本写法,包括4个组成部分(以下部分来源于网络):
- 产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个零部件。
- 抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()。
- 具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。
- 指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。
在开发中我们用到更多的是建造者模式的另一种写法,即链式写法
/**
* Builder接口
*/
public interface Builder {
Builder buildWheel();
Builder buildLights();
Builder buildSpeaker();
Product build();
}
/**
* 车的建造器
*/
public class CarBuilder implements Builder {
private Car mCar = new Car();
@Override
public CarBuilder buildWheel() {
mCar.setWheel("four Wheels");
return this;
}
@Override
public CarBuilder buildLights() {
mCar.setLights("two Lights");
return this;
}
@Override
public CarBuilder buildSpeaker() {
mCar.setSpeaker("one Speaker");
return this;
}
public Product build() {
return mCar;
}
}
/**
* 买车的客户
*/
public class Client {
public static void main(String[] args) {
System.out.println("I need a car");
Builder builder = new CarBuilder();
Car car = (Car) builder.buildWheel()
.buildLights()
.buildSpeaker()
.build();
System.out.println("I got my car " + car);
}
}
这种写法更加方便我们去定制想要的产品,源码中也有很多类似的用法,大家在日常开发中可以体会它的优势