设计模式--创建型模式

软件设计原则

设计模式--创建型模式_第1张图片

1.工厂模式(Factory)

1.1 简单工厂模式

关键字new
public class CourseFactory {
    public ICourse create(String name){
        if("java".equals(name)){
            return new JavaCourse();
        }else if("python".equals(name)){
            return new PythonCourse();
        }else {
            return null;
        }
    }
}
反射 - 按类名全路径反射
public class CourseFactory {
   public ICourse create(String className){
		try {
            if (!(null == className || "".equals(className))) {
               return (ICourse) Class.forName(className).newInstance();
           }
        }catch (Exception e){
           e.printStackTrace();
        }
        return null;
    }
}
反射 - class
public class CourseFactory {
	public ICourse create(Class clazz){
		try {
			if (null != clazz) {
				return clazz.newInstance();
			}
		} catch (Exception e) {
		    e.printStackTrace();
		}
		 return null;
	}
}

1.2工厂方法模式

设计模式--创建型模式_第2张图片

/*定义一个接口*/
public interface ICourseFactory {
    ICourse create();
}
 
/*实现类*/
public class JavaCourseFactory implements ICourseFactory {
    public ICourse create() {
        return new JavaCourse();
    }
}

/*实现类*/
public class PythonCourseFactory implements ICourseFactory {
    public ICourse create() {
        return new PythonCourse();
    }
}

1.3 抽象工厂模式

设计模式--创建型模式_第3张图片
产品族:Java、Python ……不同语言
产品等级结构:笔记、视频(每个语言都有对应的笔记、视频等共性的东西)
思路:先创建笔记、视频接口,不同语言的笔记、视频交由子类具体实现,最后将各个语言的笔记、视频整合,交由工厂统一实现。

设计模式--创建型模式_第4张图片

public interface INote {
    void edit();
}

public class JavaNote implements INote {
    public void edit() {
        System.out.println("编写Java笔记");
    }
}

public class PythonNote implements INote {
    public void edit() {
        System.out.println("编写Python笔记");
    }
}

public interface IVideo {
    void record();
}

public class JavaVideo implements IVideo {
    public void record() {
        System.out.println("录制Java视频");
    }
}

public class PythonVideo implements IVideo {
    public void record() {
        System.out.println("录制Python视频");
    }
}
public interface CourseFactory {
    INote createNote();
    IVideo createVideo();
}

public class JavaCourseFactory implements CourseFactory {
    public INote createNote() {
        return new JavaNote();
    }
    public IVideo createVideo() {
        return new JavaVideo();
    }
}

public class PythonCourseFactory implements CourseFactory {
    public INote createNote() {
        return new PythonNote();
    }
    public IVideo createVideo() {
        return new PythonVideo();
    }
}

2.单例模式(Singleton)

确保一个类有且仅有一个实例,并全局调用。
应用场景:ServletContext、ServletContextConfig、ApplicationContext、数据库连接池

2.1 饿汉式单例

类加载时,创建单例对象

优点:
线程安全,没有任何形式的锁,执行效率高。
缺点:
类加载时就初始化,不管用不用都占用内存空间。
应用:
Spring IOC容器中ApplicationContext就是典型的饿汉式单例。

public class HungrySingleton {

    private static final HungrySingleton hungrySingleton = new HungrySingleton();

    private HungrySingleton(){}	// private:防止外部new获取对象实例

    public static HungrySingleton getInstance(){
        return  hungrySingleton;
    }
}
public class HungryStaticSingleton {
    
    private static final HungryStaticSingleton hungrySingleton;

    static {		//静态代码块
        hungrySingleton = new HungryStaticSingleton();
    }

    private HungryStaticSingleton(){}		// private:防止外部new获取对象实例

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

2.2 懒汉式单例

外部类调用时内部类才会加载

volatile + synchronized双重检查锁
public class LazyDoubleCheckSingleton {

    private volatile static LazyDoubleCheckSingleton lazy = null;	// volatile

    private LazyDoubleCheckSingleton(){}

    public static LazyDoubleCheckSingleton getInstance(){
        if(lazy == null){
            synchronized (LazyDoubleCheckSingleton.class){
                if(lazy == null){
                    lazy = new LazyDoubleCheckSingleton();
                }
            }
        }
        return lazy;
    }
}

第一个if(lazy == null),只有在一开始lazy尚未初始化时要抢锁,lazy初始化之后,就不存在抢锁的情况,性能得以保证。
第二个if(lazy == null),在一开始lazy尚未初始化时,会有大量线程进入第一个if(lazy == null),并等待锁,为了保证只有第一个抢到锁的线程才能初始化lazy,所以其他线程抢到锁之后还要再判断一次。

为什么要使用volatile?

new LazyDoubleCheckSingleton()的过程包含内存分配并初始化零值、设置对象头、init方法初始化(详见JVM文档),内存分配并初始化零值时就可以将分配的内存地址赋值给lazy,然而对象可能还没有初始化,若此时另外一个线程进来,调用getInstance方法,可能获取的状态就是错的。问题的根本在于将分配的内存地址赋值给lazy和对象初始化的重排序导致,所以要依赖volatile,防止指令重排序。

静态内部类

内部类在被调用之前初始化,避免了线程安全的问题,也解决了饿汉式内存浪费的问题。

public class LazyInnerClassSingleton {
   
    private LazyInnerClassSingleton(){}

    // static 是为了使单例的空间共享
    // final保证这个方法不会被重写,重载
    public static final LazyInnerClassSingleton getInstance(){
        return LazyHolder.LAZY;
    }


    private static class LazyHolder{
        private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();                                             
    }
}

2.3 反射破坏单例

构造方法private,虽然可以防止new,但如果通过反射调用构造方法,再getInstance(),还是会产生两个不同实例。在构造方法中判断,抛异常。

public class LazyInnerClassSingleton {
   
private LazyInnerClassSingleton(){
        if(LazyHolder.LAZY != null){		// 防止反射破坏单例            
	   		throw new RuntimeException("不允许创建多个实例");
        }
    }

    // static 是为了使单例的空间共享,final保证这个方法不会被重写,重载
    public static final LazyInnerClassSingleton getInstance(){
        return LazyHolder.LAZY;
    }

    private static class LazyHolder{
        private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();                                             
    }
}

2.4 序列化破坏单例

当对象序列化到磁盘,下次用时再反序列时,会因为内存的重新分配而产生一个新的对象。
实现Serializable 接口,重写readResolve()。

public class SeriableSingleton implements Serializable {

    public final static SeriableSingleton INSTANCE = new SeriableSingleton();
    
    private SeriableSingleton(){}

    public static SeriableSingleton getInstance(){
        return INSTANCE;
    }

    private  Object readResolve(){
        return  INSTANCE;
    }
}

在ObjectInputStream类的readResolve()中,如果readResolve()重写了,则调用readResolve()。

缺陷:
其实通过源码了解到,对象实例化了两次,只不过新创建的对象没有被调用而已,所以如果需要大量的创建对象,内存开销必然很大。

2.5 枚举式单例

通过枚举的唯一标识获取实例,枚举类的源码中针对反射、序列化破坏已经做了如上处理。

public enum EnumSingleton {
    INSTANCE;
    private Object data;
    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }

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

枚举类单例如何避免反射破坏?
反射的源码中,有针对类的类型判断,如果是枚举类,则抛异常。
枚举类单例如何避免序列化破坏?
序列化的 readObject()方法中,针对枚举类的读方法中,是基于注册式单例(类+name)保证了唯一。

2.6 容器缓存式单例

通过反射获取类对象,适用于很多实例需要创建的情况。
非线程安全。

public class ContainerSingleton {
    private ContainerSingleton(){}
    private static Map ioc = 
				new ConcurrentHashMap();                                          
    public static Object getInstance(String className){
        synchronized (ioc) {
            if (!ioc.containsKey(className)) {
                Object obj = null;
                try {
                    obj = Class.forName(className).newInstance();
                    ioc.put(className, obj);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return obj;
            } else {
                return ioc.get(className);
            }
        }
    }
}

2.7 ThreadLocal线程单例

ThreadLocal创建的对象不是全局唯一的。但能保证单个线程内唯一,线程安全。

public class ThreadLocalSingleton {
    private static final ThreadLocal threadLocalInstance =
	    new ThreadLocal(){
	        @Override
	        protected ThreadLocalSingleton initialValue() {
	            return new ThreadLocalSingleton();
	        }
		};

    private ThreadLocalSingleton(){}

    public static ThreadLocalSingleton getInstance(){
        return threadLocalInstance.get();
    }
}

3.原型模式(Prototype)

拷贝对象

常用于:
1.类初始化消耗资源较多。Clone相比new的好处就在于避免了对象的初始化。
2.New 一个对象的过程非常繁琐(考虑数据、权限等)。
3.构造函数复杂
4.循环体中生产大量对象。

3.1 浅克隆

对象中引用类型clone的是地址引用,也就是说所有对象共用一个引用类型。

public interface Prototype{
    Prototype clone();
}

public class ConcretePrototypeA implements Prototype {
    private int age;
    private String name;
    private List hobbies;

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public List getHobbies() {
        return hobbies;
    }
    public void setHobbies(List hobbies) {
        this.hobbies = hobbies;
    }

    @Override
    public ConcretePrototypeA clone() {
        ConcretePrototypeA concretePrototype = new ConcretePrototypeA();
        concretePrototype.setAge(this.age);
        concretePrototype.setName(this.name);
        concretePrototype.setHobbies(this.hobbies);
        return concretePrototype;
    }
}
public class Client {
    private Prototype prototype;

    public Client(Prototype prototype){
        this.prototype = prototype;
    }
    public Prototype startClone(Prototype concretePrototype){
        return (Prototype)concretePrototype.clone();
    }
}
public class PrototypeTest {
    public static void main(String[] args) {
        // 创建一个具体的需要克隆的对象
        ConcretePrototypeA concretePrototype = new ConcretePrototypeA();
        // 填充属性,方便测试
        concretePrototype.setAge(18);
        concretePrototype.setName("prototype");
        List hobbies = new ArrayList();
        concretePrototype.setHobbies(hobbies);
        System.out.println(concretePrototype);

        // 创建Client对象,准备开始克隆
        Client client = new Client(concretePrototype);
        ConcretePrototypeA concretePrototypeClone =
		       (ConcretePrototypeA) client.startClone(concretePrototype);
        System.out.println(concretePrototypeClone);

        System.out.println("克隆对象中的引用类型地址值:" + 			 					concretePrototypeClone.getHobbies());                                  
        System.out.println("原对象中的引用类型地址值:" +       				
					concretePrototype.getHobbies());                                          
        System.out.println("对象地址比较:"+
	(concretePrototypeClone.getHobbies() == concretePrototype.getHobbies()));
    }
}

3.2 深克隆

通过序列化,重写引用类型。
继承Cloneable接口,重写clone()。
继承Serializable 接口,序列化。

public class QiTianDaSheng implements Cloneable,Serializable {

    public JinGuBang jinGuBang;

    public  QiTianDaSheng(){
        this.jinGuBang = new JinGuBang();
    }

    @Override
    protected Object clone() {
            return this.deepClone();
    }
    
    public Object deepClone(){
        try{
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            ByteArrayInputStream bis = 
			new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            return ois.readObject();
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

3.3 源码

scope=’prototype’   // 作用域
JSON.parseObject()

4.构造者模式(Builder)

将一个复杂对象的构建与它的表示分离,同样的构建过程可以创建不同的表示。用户只需要指定建造类型,无需了解建造的过程和细节。
适用于:
1、对象的构建需要很多步骤且顺序不固定
2、对象的构建结构非常复杂
3、将复杂对象的创建和使用分离

public class File {
   private String name;
   private String type;
   private String url;
   private String lastMotifile;

   public void setName(String name) {
      this.name = name;
   }

   public void setType(String type) {
      this.type = type;
   }

   public void setUrl(String url) {
      this.url = url;
   }

   public void setLastMotifile(String lastMotifile) {
      this.lastMotifile = lastMotifile;
   }

   @Override
   public String toString() {
      return "File{" +
            "name='" + name + '\'' +
            ", type='" + type + '\'' +
            ", url='" + url + '\'' +
            ", lastMotifile='" + lastMotifile + '\'' +
            '}';
   }
   
}   
public static class FileBuilder{
   private File file = new File();

   public FileBuilder setName(String name) {
      file.setName(name);
      return this;
   }

   public FileBuilder setType(String type) {
      file.setType(type);
      return this;
   }

   public FileBuilder setUrl(String url) {
      file.setUrl(url);
      return this;
   }

   public FileBuilder setLastMotifile(String lastMotifile) {
      file.setLastMotifile(lastMotifile);
      return this;
   }

   public File build(){
      return file;
   }
}

public class Client {
   public static void main(String[] args) {
     File file = new File.FileBuilder()
             .setLastMotifile("last")
             .setName("fileName")
             .setType("doc")
             .setUrl("http://")
             .build();
     System.out.println(file);
   }
}

你可能感兴趣的:(设计模式,设计模式,java,开发语言)