Java设计模式——创建型

Java设计模式——创建型

总体来说设计模式分为三大类:
  • 创建型模式,共五种:普通工厂模式、抽象工厂模式、单例模式、建造者模式、原型模式。
  • 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
  • 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
六大原则:
  • 开闭原则(Open Close Principle)对扩展开放,对修改关闭。在程序拓展时,不去修改原有代码,目的是使程序的扩展性好,易于维护和升级;要达到该效果,需要使用接口和抽象类
  • 里氏代换原则(Liskov Substitution Principle):该原则为开闭原则的基础,子类可以扩展父类的功能,但不能改变父类原有的功能;任何基类可以出现的地方,子类一定可以出现。 
  • 依赖倒置原则(Dependence Inversion Principle):该原则为开闭原则的基础,针对接口编程,依赖于抽象而不依赖于具体。
  • 接口隔离原则(Interface Segregation Principle):使用多个隔离的接口,比使用单个接口要好,可降低类间耦合度。也就是说在为各个类建立专用的接口时,不要试图去建立一个庞大的接口供所有依赖它的类去调用。多个注重单一功能的接口,可提供系统的灵活性和可维护性。
  • 迪米特法则(Demeter Principle):一个类应当尽量少的与其他类发生相互作用
  • 单一职责原则:一个类只负责一项职责
本文章先探讨创建型的设计模式。

1、普通工厂模式

——优点:
  • 使用工厂方法可以让用户的代码和某个特定类的子类的代码解耦
  • 使用者不必知道它所使用的对象是怎样被创建的,只需知道该对象有哪些方法即可
——适用情景:
  • 用户需要一个类的子类的实例,但不希望与该类的子类形成耦合
  • 用户需要一个类的子类的实例,但用户不知道该类有多少子类可用
——应用实例:
  • Spring Beanfactory:作为Spring基础的IoC容器,是Spring的一个Bean工厂。
  • java.lang.Class#forName()
  • 集合接口Collection,该接口中的iterator()方法就是一个工厂方法,返回一个实现了Iterator接口的类的实例,用户只需知道Iterator接口有哪些方法即可,不需知道返回的实例是哪个类创建的。
普通工厂模式分为三种:

1.1 单个工厂方法模式

就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。
public interface Sender {
	public void Send();
}
public class MailSender implements Sender{

	@Override
	public void Send() {
		System.out.println("this is mailSender!");
	}

}
public class SmsSender implements Sender{

	@Override
	public void Send() {
		System.out.println("this is smsSender!");
	}

}
public class SendFactory {

	public Sender produce(String type){
		if("mail".equals(type))
			return new MailSender();
		else if("sms".equals(type))
			return new SmsSender();
		else
			return null;
	}
}
public class FactoryTest {

	public static void main(String[] args) {
		SendFactory factory=new SendFactory();
		Sender sender=factory.produce("mail");
		sender.Send();
	}
	
}

1.2 多个工厂方法模式

对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。
将上面的代码做下修改,改动下SendFactory类就行,如下:
public class SendFactory {

	public Sender produceMail(){
		return new MailSender();
	}
	
	public Sender produceSms(){
		return new SmsSender();
	}
}

1.3 静态工厂方法模式(优先使用)

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

	public static Sender produceMail(){
		return new MailSender();
	}
	
	public static Sender produceSms(){
		return new SmsSender();
	}
}

2、抽象工厂模式

普通工厂模式有一个问题:如果要拓展程序,必须对工厂类进行修改,这违背了开闭原则。而抽象工厂模式,创建多个工厂类,一旦需要增加新功能,直接增加新的工厂类就可以了,不需要修改之前的代码。
——优点:
  • 可以为用户采集一系列相关的对象,使用户和创建这些对象的类脱藕
  • 扩展时不需要对原有工厂类进行修改,符合开闭原则
——适用情景:
  • 系统希望为用户提供多个对象,但不希望直接new实例化,即希望用户和创建的类脱藕
  • 系统需要为用户提供多个相关对象,以便能联合使用,但又不希望用户决定这些对象是如何关联的
  • 系统需要为用户提供一系列对象,但只需要用户知道这些对象有哪些方法可用,不需要用户知道这些对象的创建过程
——应用实例:
  • JDBC:用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它有一组Java语言编写的类和接口组成。用户可通过工厂模式来选择是mysql还是oracle
public interface Produce {
	
	public void produce();

}
实现类:
public class CPU implements Produce{
	
	private String name;

	public CPU(String name) {
		this.name=name;
	}
	
	@Override
	public void produce() {
		System.out.println("创建"+name+"_CPU!");
	}

}
public class Memory implements Produce{
	
	private String name;
	
	public Memory(String name) {
		this.name=name;
	}

	@Override
	public void produce() {
		System.out.println("创建"+name+"_Memory!");
	}

}
工厂接口:
public interface IFactory {

	public Produce createCPU(String name);
	
	public Produce createMemory(String name);
	
}
工厂实现类:
public class MacFactory implements IFactory{

	@Override
	public Produce createCPU(String name) {
		return new CPU(name);
	}

	@Override
	public Produce createMemory(String name) {
		return new Memory(name);
	}

}
public class LenovoFactory implements IFactory{

	@Override
	public Produce createCPU(String name) {
		return new CPU(name);
	}

	@Override
	public Produce createMemory(String name) {
		return new Memory(name);
	}

}
public class FactoryTest {

	public static void main(String[] args) {
		IFactory factory=new MacFactory();
		factory.createCPU("Mac").produce();
		factory.createMemory("Mac").produce();
		
		factory=new LenovoFactory();
		factory.createCPU("Lenovo").produce();
		factory.createMemory("Lenovo").produce();
	}
	
}

3、单例模式

——适用场景:
  • 当系统需要某个类只能有一个实例
  • 需要频繁实例化然后销毁的对象
——应用实例:
  • java.lang.Runtime#getRuntime()使用了单例模式,每个java程序实际上都是启动了一个JVM进程,而每个JVM进程都是对应一个Runtime实例,此实例是由JVM为其实例化的。由于Java是单进程的,所以Runtime实例应该只有一个
  • 系统资源可考虑单例,如文件路径,数据库链接,系统常量等

3.1 饿汉式单例

public class Singleton {  
    
    private Singleton(){}   //将构造函数私有化,不允许外部类直接创建对象  
  
    private static Singleton instance=new Singleton();  
      
    public static Singleton getInstance(){  
        return instance;  
    }  
}  

3.2 双检锁

public class Singleton {  
	private static volatile Singleton instance; 

	private Singleton(){}

	public static Singleton getInstance(){
		if(instance == null){
			synchronized (Singleton.class) {//1
				if(instance == null)//2
					instance = new Singleton();//3
			}  
		}  
		return instance;  
	}  
}
该方法既可以实现线程安全,又可以保证性能不受影响,解决了上述的问题,可以说是完美的。但理论是完美的,现实是残酷的。该方法在很多优化编译器上是也不完全安全,原因在于//3的代码在不同编译器上的行为是无法预知的。一个优化编译器可以合法地如下实现 instance=new Singleton():
  • 分配内存空间
  • 初始化对象
  • instance指向分配的内存空间
JVM并不保证后两个操作的先后顺序。假设两线程同时调用getInstance(),线程A先进入,在执行到//3时JVM可能先为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。这样就有可能出错了。线程A离开synchronized块后,线程B进入,B看到的是instance已经不是null了(内存已经分配),于是它开始放心地使用instance,但这个是错误的,因为A还没有来得及完成instance的初始化,而线程B就返回了未被初始化的instance实例。
解决方法是:变量instance使用volatile修饰,禁止指令重排优化(jdk1.5版本后有效)

3.3 Initialization on Demand Holder模式:使用内部类维护单例的实现

该方法使用内部类来做到延迟加载对象,而且其加载过程是线程安全的。这种写法完全使用了JVM的机制,它能保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕,这样我们就不用担心上面的问题。内部类SingletonHolder只有在getInstance()方法第一次调用的时候才会被加载。
public class Singleton{ 
	
	private Singleton() {}
	
	private static class SingletonHolder{      
		public final static Singleton instance = new Singleton();      
	}      

	public static Singleton getInstance(){      
		return SingletonHolder.instance;      
	}      
}  

4、建造者模式

——优点:
  • 使用建造者模式可以使客户端不必知道产品内部组成的细节
  • 具体建造者类之间是相互独立的,对系统的扩展非常有利,可对建造过程逐步细化,而不对其他模块产生任何影响
  • 当增加新的建造者,不必修改指挥者代码,满足开闭原则
——适用场景:
  • 创建一些复杂的对象时,这些对象的内部组成构件间的建造顺序是稳定的,但是对象的内部组成构件面临着复杂的变化
  • 当对象的创建过程必须独立于创建该对象的类时
——应用实例
  • StringBuilder和StringBuffer的append方法,但严格来说,StringBuilder并不是标准的建造者模式,因为缺少指挥类
建造者模式和工厂模式的区别:当创造一个对象需要很多步骤时适合使用建造者模式。而当只需调用一个方法就可以简单地创建整个对象时适合使用工厂模式。

产品类,由多个部件组成
public class Produce {

	ArrayList<String> parts=new ArrayList<>();
	
	public void add(String part){
		parts.add(part);
	}
	
	public void show(){
		for (String p : parts)
			System.out.println(p);
	}
}
抽象创造者类
public abstract class Builder {

	public abstract void BuildCPU();
	public abstract void BuilderMemory();
	public abstract Produce getResult();
	
}
public class MacBuilder extends Builder{
	
	private Produce produce=new Produce();

	@Override
	public void BuildCPU() {
		produce.add("Mac_CPU");
	}

	@Override
	public void BuilderMemory() {
		produce.add("Mac_Memory");
	}

	@Override
	public Produce getResult() {
		return produce;
	}

}
public class LenovoBuilder extends Builder{
	
	private Produce produce=new Produce();

	@Override
	public void BuildCPU() {
		produce.add("Lenovo_CPU");
	}

	@Override
	public void BuilderMemory() {
		produce.add("Lenovo_Memory");
	}

	@Override
	public Produce getResult() {
		return produce;
	}

}
指挥者类
public class Director {

	public static void construct(Builder builder){
		builder.BuildCPU();
		builder.BuilderMemory();
	}
	
}
public class BuilderTest {

	public static void main(String[] args) {
		Builder macBuilder=new MacBuilder();
		Director.construct(macBuilder);  //指挥者指挥创建指定产品
		Produce p1=macBuilder.getResult();
		p1.show();
		
		Builder lenovoBuilder=new LenovoBuilder();
		Director.construct(lenovoBuilder);
		Produce p2=lenovoBuilder.getResult();
		p2.show();
	}
	
}

5、原型模式

该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。一个原型类,只需要实现Cloneable接口,super.clone()调用的是Object的clone()方法。
首先需要了解对象深、浅复制的概念:
  • 浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。
  • 深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。
——优点:
  • 使用new创建代价大时,使用原型模式复制已有的实例,可以提高效率
——适用场景:
  • 从一个对象出发,得到多个和其状态相同,并相互独立的对象时
public class Rectangle implements Cloneable,Serializable{

	private static final long serialVersionUID = 1L;
	
	public double m,n;
	
	public Rectangle(double m,double n) {
		this.m=m;
		this.n=n;
	}

	public Object clone() throws CloneNotSupportedException{
		Object obj= super.clone();
		return obj;
	}
	
}
public class Geometry implements Cloneable,Serializable{
	
	private static final long serialVersionUID = 1L;
	public double height;
	public Rectangle rectangle;
	
	public Geometry(Rectangle rectangle,double height) {
		this.rectangle=rectangle;
		this.height=height;
	}
	
	public Object clone() throws CloneNotSupportedException{
		Geometry object=(Geometry) super.clone();
		object.rectangle=(Rectangle) rectangle.clone();
		return object;
	}
	
	/* 深复制  当原型类较复杂是,采用流的形式读入当前对象的二进制输入,再写出二进制数据对应的对象*/ 
	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 class ProtoTest {

	public static void main(String[] args) throws CloneNotSupportedException, ClassNotFoundException, IOException {
		Geometry g=new Geometry(new Rectangle(2, 4),5);
		Geometry cg1=(Geometry) g.clone();
		System.out.println(g.height==cg1.height);  //为true;
		System.out.println(g.rectangle==cg1.rectangle);  //为false
		
		Geometry cg2=(Geometry) g.deepClone();
		System.out.println(g.height==cg2.height);  //为true;
		System.out.println(g.rectangle==cg2.rectangle);  //为false
	}
}



猜你喜欢

转载自blog.csdn.net/hjy132/article/details/78304257