• 设计模式——创建型


    一、 简单工厂

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

    适用:

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

    优点:

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

    缺点:

    • 工厂类的职责相对过重,增加新产品需要修改工厂类的判断逻辑,违背开闭原则
    • 无法形成基于继承的等级结构
    // 简单判断
    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);
    		}
    	}
    }
    
  • 相关阅读:
    026.MFC_发送消息
    021.MFC_字体和颜色对话框
    020.MFC_文件对话框
    015.MFC_Menu
    qt http get post实现
    openssl error: unrecognized command line option '-m64'
    ModbusTCP协议
    013.MFC_TreeControl
    菱形继承问题以及解决
    ffmpeg
  • 原文地址:https://www.cnblogs.com/JL916/p/12642994.html
Copyright © 2020-2023  润新知