设计模式——创建型

一、 简单工厂

由一个工厂对象决定创建出哪一种产品类的实例

适用:

  • 工厂类负责创建的对象比较少
  • 应用层只知道传入工厂类的参数,不关心如何创建对象

优点:

  • 只需要传入一个正确的参数,就可以获取对象,不需要知道细节

缺点:

  • 工厂类的职责相对过重,增加新产品需要修改工厂类的判断逻辑,违背开闭原则
  • 无法形成基于继承的等级结构
// 简单判断
public Video getVideo(String type) {
	if ("java".equalsIgnoreCase(type)) {
		return new JavaVideo();
	} else if ("python".equalsIgnoreCase(type)) {
		return new PythonVideo();
	}
	return null;
}

// 基于反射 弥补开闭原则
public Video getVideo(Class c) {
	Video video = null;
	try {
		video = (Video) Class.forName(c.getName()).newInstance();
	} catch (InstantiationException e) {
		e.printStackTrace();
	} catch (IllegalAccessException e) {
		e.printStackTrace();
	} catch (ClassNotFoundException e) {
		e.printStackTrace();
	}
	return video;
}

二、 工厂方法

定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类

工厂方法让类的实例化推迟到子类中进行

适用:

  • 创建对象需要大量重复的代码
  • 应用层不依赖与产品实例如何被创建等细节
  • 一个类通过其子类来指定创建哪个对象

优点:

  • 用户只需要关心所需产品对应的工厂,无需关心创建细节
  • 加入新产品符合开闭原则,提高可扩展性

缺点:

  • 类的个数容易过多,增加复杂度
  • 增加类系统的抽象性和理解难度
public abstract class VideoFactory {

    public abstract Video getVideo();
}

public class JavaVideoFactory extends VideoFactory {

	@Override
	public Video getVideo() {
		return new JavaVideo();
	}
}

public class PythonVideoFactory extends VideoFactory {

	@Override
	public Video getVideo() {
		return new PythonVideo();
	}
}

三、 抽象工厂

抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口

适用:

  • 应用层不依赖于产品类实例如何被创建实现等细节
  • 强调一系列相关的产品对象(同一产品族)一起使用创建对象需要大量重复的代码
  • 提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现

优点:

  • 具体产品在应用层代码隔离,无需关心创建细节
  • 一个系列的产品族一起创建

缺点:

  • 规定了所有可能被创建的产品集合,产品族中扩展新的产品需要修改抽象工厂的接口
  • 增加系统的抽象性和复杂度

同一产品族:不同类,有一定关系,属于同一系列
同一产品等级结构:同一个类的子类,不同实现

抽象工厂关注产品族
工厂方法关注产品等级结构

public interface CourseFactory {
	Video getVideo();

	Article getArticle();
}

public class JavaCourseFactory implements CourseFactory {

	@Override
	public Video getVideo() {
		return new JavaVideo();
	}

	@Override
	public Article getArticle() {
		return new JavaArticle();
	}
}

public class PythonCourseFactory implements CourseFactory {

	@Override
	public Video getVideo() {
		return new PythonVideo();
	}

	@Override
	public Article getArticle() {
		return new PythonArticle();
	}
}

四、 建造者模式

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

适用:

  • 对象有非常复杂的内部结构
  • 想把复杂对象的创建和适用分离

优点:

  • 封装性好,创建和适用分离
  • 扩展性好,建造类间独立,一定程度上解耦

缺点:

  • 产生多余的Builder对象
  • 产品内部发生变化,建造者都要修改,成本较大
// v1
public abstract class CourseBuilder {

	public abstract void buildCourseName(String courseName);

	public abstract void buildCoursePPT(String coursePPT);

	public abstract void buildCourseVideo(String courseVideo);

	public abstract Course makeCourse();
}

public class JavaCourseBuilder extends CourseBuilder {
	private Course course = new Course();

	@Override
	public void buildCourseName(String courseName) {
		course.setCourseName(courseName);
	}

	@Override
	public void buildCoursePPT(String coursePPT) {
		course.setCoursePPT(coursePPT);
	}

	@Override
	public void buildCourseVideo(String courseVideo) {
		course.setCourseVideo(courseVideo);
	}

	@Override
	public Course makeCourse() {
		return course;
	}
}

public class Coach {
	private CourseBuilder courseBuilder;

	public void setCourseBuilder(CourseBuilder courseBuilder) {
		this.courseBuilder = courseBuilder;
	}

	public Course makeCourse(String courseName, String coursePPT, String courseVideo, String courseArticle,
			String courseQA) {
		courseBuilder.buildCourseName(courseName);
		courseBuilder.buildCoursePPT(coursePPT);
		courseBuilder.buildCourseVideo(courseVideo);
		return courseBuilder.makeCourse();
	}
}

public class Test {
	public static void main(String[] args) {
		CourseBuilder courseBuilder = new JavaCourseBuilder();
		Coach coach = new Coach();
		coach.setCourseBuilder(courseBuilder);

		Course course = coach.makeCourse("Java设计模式精讲", "Java设计模式精讲PPT", "Java设计模式精讲视频");
		System.out.println(course);
	}
}
// v2 链式调用
public class Course {

	private String courseName;
	private String coursePPT;
	private String courseVideo;

	public Course(CourseBuilder courseBuilder) {
		this.courseName = courseBuilder.courseName;
		this.coursePPT = courseBuilder.coursePPT;
		this.courseVideo = courseBuilder.courseVideo;
	}

	public static class CourseBuilder {
		private String courseName;
		private String coursePPT;
		private String courseVideo;

		public CourseBuilder buildCourseName(String courseName) {
			this.courseName = courseName;
			return this;
		}

		public CourseBuilder buildCoursePPT(String coursePPT) {
			this.coursePPT = coursePPT;
			return this;
		}

		public CourseBuilder buildCourseVideo(String courseVideo) {
			this.courseVideo = courseVideo;
			return this;
		}

		public Course build() {
			return new Course(this);
		}
	}
}

public class Test {
    public static void main(String[] args) {
        Course course = new Course.CourseBuilder().buildCourseName("Java设计模式精讲").buildCoursePPT("Java设计模式精讲PPT").buildCourseVideo("Java设计模式精讲视频").build();
        System.out.println(course);
    }
}

五、 单例模式

保证一个类仅有一个实例,并提供一个全局访问点

适用:

  • 确保任何情况下都绝对只有一个实例

优点:

  • 在内存里只有一个实例,减少类内存开销
  • 避免对资源的多重占用
  • 设置全局访问点,严格控制访问

缺点:

  • 没有接口,扩展困难

重点:

  • 私有构造器
  • 线程安全
  • 延迟加载
  • 序列化和反序列化
  • 反射

懒汉式:延迟加载

// 方式1:synchronized 保证线程安全
// 缺陷:synchronized 耗费资源
public class LazySingleton {

	private static LazySingleton lazySingleton = null;

	private LazySingleton() {
        // 私有构造器 保证不会被其他类调用
	}

	public synchronized static LazySingleton getInstance(){
		if(lazySingleton == null){
			lazySingleton = new LazySingleton();
		}
		return lazySingleton;
	}
}
// 方式2:volatile 防止指令重排序 + 双重检查减少锁的使用频率
// 不加 volatile:其他线程可能访问到未初始化完成的单例对象
public class LazySingleton {

	private volatile static LazySingleton lazyDoubleCheckSingleton = null;

	private LazySingleton(){

	}

	public static LazySingleton getInstance() {
		if(lazyDoubleCheckSingleton == null) {
			synchronized (LazySingleton.class) {
				if(lazyDoubleCheckSingleton == null){
					lazyDoubleCheckSingleton = new LazySingleton();
				}
			}
		}
		return lazyDoubleCheckSingleton;
	}
}
// 方式3:静态内部类
// 类的初始化阶段会加锁 因此线程安全
public class LazySingleton {
    
    private LazySingleton(){
        
    }

    // 私有
    private static class Holder {
        private static LazySingleton staticInnerClassSingleton = new LazySingleton();
    }
    
    public static LazySingleton getInstance(){
        return Holder.staticInnerClassSingleton;
    }
}

饿汉式:类加载即赋值

public class HungrySingleton {

    private static HungrySingleton hungrySingleton;

    static {
        hungrySingleton = new HungrySingleton();
    }

    private HungrySingleton(){
        
    }

    public static HungrySingleton getInstance() {
        return hungrySingleton;
    }
}

问题1:序列化再反序列化得到的是不同的对象

// 以饿汉式为例
HungrySingleton instance = HungrySingleton.getInstance();

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
oos.writeObject(instance);

File file = new File("singleton_file");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));

HungrySingleton newInstance = (HungrySingleton) ois.readObject();

System.out.println(instance == newInstance);	// false

解决:

public class HungrySingleton implements Serializable {

	// ... 

    private Object readResolve(){
        return hungrySingleton;
    }
}

得到同一个对象instance == newInstance,但是在过程中创建了对象。

问题2:通过反射开放构造器权限

Class objectClass = HungrySingleton.class;
Constructor constructor = objectClass.getDeclaredConstructor();
constructor.setAccessible(true);

HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();
HungrySingleton instance = HungrySingleton.getInstance();	// 注意顺序

System.out.println(instance == newInstance);

解决:

// 对于 类加载时就实例化的单例实现,即饿汉式/静态内部类
private hungrySingleton() {
	if(hungrySingleton != null){
		throw new RuntimeException("单例构造器禁止反射调用");
	}
}

// 而对于其他情况,如双重检查饿汉式,仍然会创建两个对象 无法避免

枚举:完美解决所有问题

  • 天然支持序列化/反序列化
  • 只有一个构造器,且不能通过反射调用
public enum EnumInstance {
	INSTANCE {
		@Override
		protected void printTest() {
			System.out.println("Print Test");
		}
	};

	protected abstract void printTest();

	private Object data;

	public Object getData() {
		return data;
	}

	public void setData(Object data) {
		this.data = data;
	}

	public static EnumInstance getInstance() {
		return INSTANCE;
	}
}

容器:统一管理多个单例,由 key 保证单一

public class ContainerSingleton {

	private ContainerSingleton() {

	}

	private static Map<String, Object> singletonMap = new HashMap<String, Object>();

	public static void putInstance(String key, Object instance) {
		if (StringUtils.isNotBlank(key) && instance != null) {
			if (!singletonMap.containsKey(key)) {
				singletonMap.put(key, instance);
			}
		}
	}

	public static Object getInstance(String key) {
		return singletonMap.get(key);
	}

}

ThreadLocal:线程单例

public class ThreadLocalInstance {

	private static final ThreadLocal<ThreadLocalInstance> threadLocalInstanceThreadLocal
	= new ThreadLocal<ThreadLocalInstance>(){
		@Override
		protected ThreadLocalInstance initialValue() {
			return new ThreadLocalInstance();
		}
	};
	
	private ThreadLocalInstance(){

	}

	public static ThreadLocalInstance getInstance(){
		return threadLocalInstanceThreadLocal.get();
	}
}

六、 原型模式

原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,不调用构造函数

适用:

  • 类初始化消耗较多资源
  • new 产生一个对象需要繁琐过程(数据准备,访问权限)
  • 构造函数复杂
  • 循环体产生大量对象

优点:

  • 性能比 new 高
  • 简化创建过程

缺点:

  • 必须重写 clone
  • 对克隆复杂对象或克隆出的对象进行复杂改造时,容易引入风险
  • 深拷贝,浅拷贝得当
public class Mail implements Cloneable {
	private String name;
	private String emailAddress;
	private String content;

	public Mail() {
		System.out.println("Mail Class Constructor");
	}

	// get and set

	@Override
	protected Object clone() throws CloneNotSupportedException {
		System.out.println("clone mail object");
		return super.clone();
	}
}

public class Test {
	public static void main(String[] args) throws CloneNotSupportedException {
		Mail mail = new Mail();
		mail.setContent("初始化模板");
		System.out.println("初始化mail:" + mail);
		for (int i = 0; i < 10; i++) {
			Mail mailTemp = (Mail) mail.clone();
			mailTemp.setName("姓名" + i);
			mailTemp.setEmailAddress("姓名" + i + "@imooc.com");
			mailTemp.setContent("恭喜您,此次慕课网活动中奖了");
			System.out.println(mailTemp);
		}
	}
}
原文地址:https://www.cnblogs.com/JL916/p/12642994.html