Java设计模式---(创建型模式)工厂、单例、建造者、原型

目录


前言

创建型模式(4种):用于描述"怎样创建对象",它的主要特点是"将对象的创建与使用分离"。

一、工厂模式(Factory)

1.1 工厂方法模式(Factory Method)

1.1.1 普通工厂方法模式

普通工厂方法模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。首先看下关系图:

举一个发送邮件和短信的例子,首先,创建二者的共同接口:

java 复制代码
public interface Sender {
	public void Send();
}

其次,创建实现类:

java 复制代码
public class MailSender implements Sender {
	@Override
	public void Send() {
		System.out.println("this is mailsender!");
	}
}
java 复制代码
public class SmsSender implements Sender {
 
	@Override
	public void Send() {
		System.out.println("this is sms sender!");
	}
}

最后,建工厂类:

java 复制代码
public class SendFactory {
 
	public Sender produce(String type) {
		if ("mail".equals(type)) {
			return new MailSender();
		} else if ("sms".equals(type)) {
			return new SmsSender();
		} else {
			System.out.println("请输入正确的类型!");
			return null;
		}
	}
}

测试下:

java 复制代码
public class FactoryTest {
 
	public static void main(String[] args) {
		SendFactory factory = new SendFactory();
		Sender sender = factory.produce("sms");
		sender.Send();
	}
}

输出:this is sms sender!

1.1.2 多个工厂方法模式

多个工厂方法模式,是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。关系图:

将上面的代码做下修改,改动下SendFactory类就行,如下:

java 复制代码
public class SendFactory {
	
	public Sender produceMail(){
		return new MailSender();
	}
	
	public Sender produceSms(){
		return new SmsSender();
	}
}

测试类如下:

java 复制代码
public class FactoryTest {
 
	public static void main(String[] args) {
		SendFactory factory = new SendFactory();
		Sender sender = factory.produceMail();
		sender.Send();
	}
}

输出:this is mailsender!

1.1.3 静态工厂方法模式

静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。

java 复制代码
public class SendFactory {
	
	public static Sender produceMail(){
		return new MailSender();
	}
	
	public static Sender produceSms(){
		return new SmsSender();
	}
}
java 复制代码
public class FactoryTest {
 
	public static void main(String[] args) {	
		Sender sender = SendFactory.produceMail();
		sender.Send();
	}
}

输出:this is mailsender!

工厂模式适合:凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。

在以上的三种模式中,第一种如果传入的字符串有误,不能正确创建对象;

第三种相对于第二种,不需要实例化工厂类;

所以,大多数情况下,我们会选用第三种------静态工厂方法模式。

1.2 抽象工厂模式(Abstract Factory)

工厂方法模式的问题是类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。因为抽象工厂不太好理解,先看看图,然后就和代码,就比较容易理解。

java 复制代码
public interface Sender {
	public void Send();
}

两个实现类:

java 复制代码
public class MailSender implements Sender {
	@Override
	public void Send() {
		System.out.println("this is mailsender!");
	}
}
java 复制代码
public class SmsSender implements Sender {
 
	@Override
	public void Send() {
		System.out.println("this is sms sender!");
	}
}

两个工厂类:

java 复制代码
public class SendMailFactory implements Provider {
	
	@Override
	public Sender produce(){
		return new MailSender();
	}
}
java 复制代码
public class SendSmsFactory implements Provider{
 
	@Override
	public Sender produce() {
		return new SmsSender();
	}
}

再提供一个接口:

java 复制代码
public interface Provider {
	public Sender produce();
}

测试类:

java 复制代码
public class Test {
 
	public static void main(String[] args) {
		Provider provider = new SendMailFactory();
		Sender sender = provider.produce();
		sender.Send();
	}
}

其实这个模式的好处就是,如果你现在想增加一个功能:发及时信息,则只需做一个实现类,实现Sender接口,同时做一个工厂类,实现Provider接口,就OK了,无需去改动现成的代码。这样做,拓展性较好!


二、单例模式(Singleton)

保证被创建一次,节省系统开销

  • 饿汉式:上来不管有没有对象,都要直接创建一个新的对象。
  • 懒汉式:首先判断有没有创建对象,如果创建对象了就使用原来创建的对象,没有创建的话新创建一个对象。

单例模式重点在于在整个系统上共享一些创建时较耗资源的对象。整个应用中只维护一个特定类实例,它被所有组件共同使用。Java.lang.Runtime是单例模式的经典例子。

java 复制代码
public static Singleton getInstance() {
		if (instance == null) {
			synchronized (instance) {
				if (instance == null) {
					instance = new Singleton();
				}
			}
		}
		return instance;
	}

使用内部类来维护单例的实现,JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕,一个完美的单例模式

java 复制代码
public class Singleton {
 
	/* 私有构造方法,防止被实例化 */
	private Singleton() {
	}
 
	/* 此处使用一个内部类来维护单例 */
	private static class SingletonFactory {
		private static Singleton instance = new Singleton();
	}
 
	/* 获取实例 */
	public static Singleton getInstance() {
		return SingletonFactory.instance;
	}
 
	/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
	public Object readResolve() {
		return getInstance();
	}
}

三、建造者模式(Builder)

工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性,其实建造者模式就是前面抽象工厂模式和最后的Test结合起来得到的。

还和前面一样,一个Sender接口,两个实现类MailSender和SmsSender。最后,建造者类如下:

java 复制代码
public class Builder {
	
	private List<Sender> list = new ArrayList<Sender>();
	
	public void produceMailSender(int count){
		for(int i=0; i<count; i++){
			list.add(new MailSender());
		}
	}
	
	public void produceSmsSender(int count){
		for(int i=0; i<count; i++){
			list.add(new SmsSender());
		}
	}
}

测试类:

java 复制代码
public class Test {
 
	public static void main(String[] args) {
		Builder builder = new Builder();
		builder.produceMailSender(10);
	}
}

建造者模式将很多功能集成到一个类里,这个类可以创造出比较复杂的东西。所以与工程模式的区别就是:

  • 工厂模式关注的是创建单个产品,
  • 而建造者模式则关注创建符合对象,多个部分。
    因此,是选择工厂模式还是建造者模式,依实际情况而定。

四、原型模式(Prototype)

原型模式虽然是创建型的模式,但是与工程模式没有关系,该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。在Java中,复制对象是通过clone()实现的,先创建一个原型类:

java 复制代码
public class Prototype implements Cloneable {
 
	public Object clone() throws CloneNotSupportedException {
		Prototype proto = (Prototype) super.clone();
		return proto;
	}
}

一个原型类,只需要实现Cloneable接口,覆写clone方法,此处clone方法可以改成任意的名称,因为Cloneable接口是个空接口,你可以任意定义实现类的方法名,如cloneA或者cloneB,因为此处的重点是super.clone()这句话,super.clone()调用的是Object的clone()方法。

将结合对象的浅复制和深复制来说一下,首先需要了解对象深、浅复制的概念:

  • 浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。
  • 深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。

写一个深浅复制的例子:

java 复制代码
public class Prototype implements Cloneable, Serializable {
 
	private static final long serialVersionUID = 1L;
	private String string;
 
	private SerializableObject obj;
 
	/* 浅复制 */
	public Object clone() throws CloneNotSupportedException {
		Prototype proto = (Prototype) super.clone();
		return proto;
	}
 
	/* 深复制 */
	public Object deepClone() throws IOException, ClassNotFoundException {
 
		/* 写入当前对象的二进制流 */
		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();
	}
 
	public String getString() {
		return string;
	}
 
	public void setString(String string) {
		this.string = string;
	}
 
	public SerializableObject getObj() {
		return obj;
	}
 
	public void setObj(SerializableObject obj) {
		this.obj = obj;
	}
 
}
 
class SerializableObject implements Serializable {
	private static final long serialVersionUID = 1L;
}

深复制,需要采用流的形式读入当前对象的二进制输入,再写出二进制数据对应的对象。

相关推荐
繁星璀璨G5 分钟前
C++11标准模板(STL)- 常用数学函数 - 计算e的给定幂 (ex)(std::exp, std::expf, std::expl)
开发语言·c++·算法·stl·计算e的给定幂
hong_zc6 分钟前
JDBC 编程
java·数据库·mysql
Flying_Fish_roe7 分钟前
MyBatis-Plus 常见问题与优化
java·tomcat·mybatis
X² 编程说11 分钟前
14.面试算法-字符串常见算法题(三)
java·数据结构·后端·算法·面试
阑梦清川34 分钟前
C++初阶-list用法总结
开发语言·c++·stl·list
wheeldown41 分钟前
【数据结构&&C语言】【入门】【首次万字详细解析】入门阶段数据结构可能用到的C语言知识,一章让你看懂数据结构!!!!!!!
c语言·开发语言·数据结构
imc.1144 分钟前
初识linux(2)
java·linux·数据库
武子康44 分钟前
大数据-143 - ClickHouse 集群 SQL 超详细实践记录!
java·大数据·数据库·分布式·sql·clickhouse·flink
巧手打字通1 小时前
解锁Java线程池:实战技巧与陷阱规避
java·性能优化·线程池
装不满的克莱因瓶1 小时前
【微服务】Eureka的自我保护机制
java·spring cloud·云原生·eureka·注册中心·服务注册