【Java】设计模式--创建型模式

创建型模式


目录

工厂模式

普通的工厂(参数是字符串)

☆多个方法工厂(没有字符串参数,调用不同方法作为传递参数方式 --> 工厂多个方法)

☆☆静态工厂方法模式(最优解BEST)

抽象工厂模式

单例模式

一般单例模式

☆☆枚举类型(最佳实现单例)

建造者模式

Builder:套餐规范

ConcurrentBuilder:每个套餐应该说明(实现)套餐的每个内容是什么

Director:根据套餐菜单,返回出对应套餐的食物

Product:最后得到的食物

测试:拿到菜单A(mealA)后点餐,得到meal,不用知道是怎么组成这个对象meal的


 

工厂模式

*组成:

  • 产品规范:一个接口(抽象类)
  • 产品:实现此接口(继承抽象类)的类
  • 工厂:根据“参数”返回不同实现(继承)类的类

*流程:

用户需要某个产品(实现类),使用对应“参数” --> 工厂 --> 工厂根据“参数”产生不同的对象

*好处:

只需要传递“参数”给工厂,就可以得到某种类,不需要自己去实现

*缺点:

如果要增加产品(实现类),也就是工厂要返回的对象种类变多,就要修改工厂,违反了开闭原则(对扩展开放/修改关闭)

 

普通的工厂(参数是字符串)

//规范实现类的接口
interface Car{
	public abstract void run();
	
	public abstract void stop();
}

//实现类A(用户得到的产品)
class Benz implements Car{

	@Override
	public void run() {
		System.out.println("Benz的run()方法");
		
	}

	@Override
	public void stop() {
		System.out.println("Benz的stop()方法");
	}
	
}

//实现类B(用户得到的产品)
class BMW implements Car{

	@Override
	public void run() {
		System.out.println("BMW的run()方法");
		
	}

	@Override
	public void stop() {
		System.out.println("BMW的stop()方法");
	}
	
}

//工厂类
class Factory{
	public  Car getCarInstance(String car) {
		Car c = null;
		if("Benz".equals(car)) {
			c = new Benz();
		}else if("BMW".equals(car)) {
			c = new BMW();
		}
		return c;
	}
}

//测试 是否工厂能够正确的生产产品(实现类)
public class FactoryPattern {
	
	public static void main(String[] args) {
		Factory f = new Factory();
		Car c = f.getCarInstance("Benz");
		c.run();
		c.stop();
	}
	
}

 

☆多个方法工厂(没有字符串参数,调用不同方法作为传递参数方式 --> 工厂多个方法)

*好处:

不用再傻乎乎的传参数给工厂了,因为参数可能传错,调用方法的话编译阶段就可以知道错误了。

对于上面的普通工厂,改动工厂类即可

class FactoryB{
	//多个方法工厂
	public Car getBenz() {
		return new Benz();
	}
	
	public Car getBMW() {
		return new BMW();
	}
}
public class FactoryPattern {
	
	public static void main(String[] args) {
		FactoryB factory = new FactoryB();
		Car c = factory.getBMW();
		c.run();
		c.stop();
	}
	
}

 

☆☆静态工厂方法模式(最优解BEST)

将多个方法工厂里面方法设置成静态

*好处:

想了想,工厂好像除了调用方法返回对象之后就没什么用了,为什么不用静态方法呢,这样就少创建了一个工厂实例咯(#^.^#)

class FactoryC{
	//静态工厂
	public static Car getBenz() {
		return new Benz();
	}
	
	public static Car getBMW() {
		return new BMW();
	}
}

public class FactoryPattern {
	
	public static void main(String[] args) {
		Car c = FactoryC.getBenz();
		c.run();
		c.stop();
	}
	
}

 

*总而言之,静态工厂方法模式是最优最优的,因为没有工厂实例(节省空间了),而且不用传字符串参数,减少出错

 

 

抽象工厂模式

为了解决工厂模式拓展业务(工厂产生新种类对象)时要修改工厂的问题,

使用抽象工厂模式,一旦需要增加新的种类的实现类,直接增加新的工厂,不需要修改之前的代码

 

*组成:

  • 产品规范:一个接口(抽象类)
  • 产品:实现此接口(继承抽象类)的类
  • 工厂:一个工厂对应一个对象,直接返回这个工厂唯一的产品(对象)
  • 抽象工厂:实现此接口的工厂才可以返回其工厂的产品给用户,管理所有的工厂

*流程:

用户传递工厂名 --> 抽象工厂根据工厂名找到工厂 --> 不同的工厂直接返回不同的对象(产品)

*好处:

一旦需要增加新的种类(新的产品),直接增加新的工厂就好,不用修改工厂代码。

//产品规范
interface FillColor{
	public void Fill();
}

//具体产品A(实现类)
class Pink implements FillColor{
	@Override
	public void Fill() {
		// TODO Auto-generated method stub
		System.out.println("Filling Pink");
	}
}

//具体产品B(实现类)
class Blue implements FillColor{
	@Override
	public void Fill() {
		// TODO Auto-generated method stub
		System.out.println("Filling Blue");
	}
}

//工厂的规范,工厂调用这个方法就产出产品
abstract class AbstractFactory{
	public abstract FillColor ProvideProduct();
}

//工厂A以及产出A产品
class FactoryPink extends AbstractFactory{
	@Override
	public FillColor ProvideProduct() {
		// TODO Auto-generated method stub
		return new Pink();
	}
}

//工厂B以及产出B产品
class FactoryBlue extends AbstractFactory{
	@Override
	public FillColor ProvideProduct() {
		// TODO Auto-generated method stub
		return new Blue();
	}
}

public class AbstractFactoryPattern {
	
	public static void main(String[] args) {
		AbstractFactory factory = new FactoryPink();
		FillColor pink = factory.ProvideProduct();
		pink.Fill();
	}
	
}

 

单例模式

*好处:

  • 某些类的创建比较频繁(各种成员属性),并且对于大型对象创建很麻烦,就可以使用单例
  •  
  • 作为通信的媒介,数据共享,可以在不建立直接关联的条件下,让多个不相关的线程或者多个进程之间进行通信

*缺点:

  • 没有抽象层,扩展困难,需要扩展的话要修改源代码
  • 职责过重,既要负责工厂方法又要提供业务方法,违背单一职责原则
  • 实例化的对象长时间不被利用可能被GC

 

static Instance原因

  • 要被静态方法getInstance()调用
  • static变量存储在方法区,每次都指向同一个变量

 

一般单例模式

懒汉式

public class Singleton {
	//赋值为null,实现延迟加载
	private static Singleton instance = null;
	
    //私有构造方法 防止被实例化(直接new)
	private Singleton() {
		
	}
	
	private static Singleton getInstance() {
		if(instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
	
}

不用想,肯定有多线程问题,所以一般想到的就是改进①:synchronized关键字

	private static synchronized Singleton getInstance() {
		if(instance == null) {
			instance = new Singleton();
		}
		return instance;
	}

这样每次调用此方法时锁住的是整个对象,性能下降,但是我们只需要在第一次创建对象时才锁住对象

改进②双检锁:

	private static Singleton getInstance() {
		if(instance == null) {
			synchronized(instance) {
				if(instance == null) {
					instance = new Singleton();
				}
			}
		}
		return instance;
	}

好像解决了问题,但是instance = new Singleton()这一句不是原子操作,分为三步

  • 1:给instance分配内存
  • 2:调用Singleton构造函数初始化成员变量
  • 3:将instance对象指向分配的内存空间(此时instance ≠ null)

由于JVM的指令重排序优化机制,所以最终执行时是1-3-2。

如果A进程执行完3就切换进程了,进程B执行时发现instance不为空,调用时发现还未初始化就报错了

所以要禁止重排序,很容易就想到可以用volatile修饰instance达到效果(也就是写操作A优先于读操作B)

但是特别注意在 Java 5 以前的版本使用了 volatile 的双检锁还是有问题的。其原因是 Java 5 以前的 JMM (Java 内存模型)是存在缺陷的,即时将变量声明成 volatile 也不能完全避免重排序,主要是 volatile 变量前后的代码仍然存在重排序问题。这个 volatile 屏蔽重排序的问题在 Java 5 中才得以修复,所以在这之后才可以放心使用 volatile。
--------------------- 
作者:一往无前-千夜 
来源:CSDN 
原文:https://blog.csdn.net/wolfking0608/article/details/69066773 
版权声明:本文为博主原创文章,转载请附上博文链接!

 

改进③final:饿汉式,第一次加载类到内存就会被初始化

*优点:线程安全,调用效率高(没有锁)

*缺点:不能延时加载,空间换时间,浪费内存

public class Singleton{
    //类加载时就初始化
    private static final Singleton instance = new Singleton();
    
    private Singleton(){}
 
    public static Singleton getInstance(){
        return instance;
    }
}

没有延迟加载的结果 --> 某些情况使用不了:譬如 Singleton 实例的创建是依赖参数或者配置文件的,在 getInstance() 之前必须调用某个方法设置参数给它,那样这种单例写法就无法使用了

 

☆☆改进④静态内部类实现:延迟加载

*静态内部类:调用时(执行getInstance())才会加载,外部类加载了也不会加载

public class Singleton {
	 
	private Singleton() {
	}
 
	/* 此处使用一个内部类来维护单例 */
	private static class SingletonFactory {
		private static final Singleton instance = new Singleton();
	}
 
	/* 获取实例 */
	public static Singleton getInstance() {
		return SingletonFactory.instance;
	}

}

 

改进④:创建与获取分离

静态内部类的另外一种实现,将获取单例与创建单例分成两个函数

public class SingletonTest {
 
	private static SingletonTest instance = null;
 
	private SingletonTest() {
	}
 
	private static synchronized void syncInit() {
		if (instance == null) {
			instance = new SingletonTest();
		}
	}
 
	public static SingletonTest getInstance() {
		if (instance == null) {
			syncInit();
		}
		return instance;
	}
}

 

☆☆枚举类型(最佳实现单例)

*优点:简单,不用像上面考虑过多多线程问题

*缺点:没有使用延迟加载,在静态代码块初始化了

实现单例的核心是构造方法私有化,而在枚举类型中构造方法本身就是私有的,且枚举类型是线程安全的

public enum EnumSingleton{
    INSTANCE;
}

直接通过EnumSingleton.INSTANCE就可以访问单例,VERY EASY.

 

**枚举类型的实现:反编译class文件

P.S. jd-gui会直接反编译成enum

package com.create;


public final class Color extends Enum
{

    public static Color[] values()
    {
        return (Color[])$VALUES.clone();
    }

    public static Color valueOf(String s)
    {
        return (Color)Enum.valueOf(com/create/Color, s);
    }

    private Color(String s, int i)
    {
        super(s, i);
    }

    public static final Color INSTANCE;
    private static final Color $VALUES[];

    static 
    {
        INSTANCE = new Color("INSTANCE", 0);
        $VALUES = (new Color[] {
            INSTANCE
        });
    }
}

可以看到,类似于饿汉式使用static 和 final修饰唯一对象INSTANCE

以及在静态代码块中初始化(Java类的加载和初始化过程都是线程安全的,所以INSTANCE也是确保了线程安全

所以使用enum枚举实现单例模式不能延迟加载

 

 

建造者模式

相比于工厂模式,工厂模式是提供单个类的不同实现方式(饮料的实现:可乐/雪碧),而建造者模式是将这些不同的类再组合在一个类(饮料+食物)类似于套餐

*流程:

根据已有的套餐规范(Builder)已创建套餐菜单A/B -->用户拿着套餐菜单A(ConcreteBuilder

---> 找服务员Waiter(Director)根据此创造对应套餐Meal(Product

 

*好处:

只需要拿到套餐菜单(ConcurrentBuilder)给Waiter(Director),就可以得到组合的对象

 

Builder:套餐规范

 

//抽象建造者Builder
abstract class MealBuilder{
	Meal meal = new Meal();
	
	public abstract void buildFood();
	
	public abstract void buildDrink();
	
	public Meal getMeal() {
		return meal;
	}
}

ConcurrentBuilder:每个套餐应该说明(实现)套餐的每个内容是什么

//具体建造者A ConcreteBuilder
class MealA extends MealBuilder{

	@Override
	public void buildFood() {
		// TODO Auto-generated method stub
		meal.setFood("汉堡");
	}
	
	@Override
	public void buildDrink() {
		// TODO Auto-generated method stub
		meal.setDrink("可乐");
	}
}
//具体建造者B ConcreteBuilder
class MealB extends MealBuilder{

	@Override
	public void buildFood() {
		// TODO Auto-generated method stub
		meal.setFood("兰州拉面");
	}
	
	@Override
	public void buildDrink() {
		// TODO Auto-generated method stub
		
	}
}

Director:根据套餐菜单,返回出对应套餐的食物

//Director
class Waiter {
	private MealBuilder mealBuilder;
	
	public Waiter(MealBuilder mealBuilder) {
		this.mealBuilder = mealBuilder;
	}
	
	public Meal construct() {
		mealBuilder.buildFood();
		mealBuilder.buildDrink();
		return mealBuilder.getMeal();
	}
	
}

Product:最后得到的食物

//Product
class Meal{
	
	private String food;
	private String drink;
	
	public String getFood() {
		return food;
	}
	public void setFood(String food) {
		this.food = food;
	}
	public String getDrink() {
		return drink;
	}
	public void setDrink(String drink) {
		this.drink = drink;
	}
}

测试:拿到菜单A(mealA)后点餐,得到meal,不用知道是怎么组成这个对象meal的

public class Builder {
	public static void main(String[] args) {
		MealA mealA = new MealA();
		Waiter w = new Waiter(mealA);
		Meal m = w.construct();
		System.out.println("食物:" + m.getFood() + " 饮料:" + m.getDrink());
		
	}
}

 

原型模式

首先创建一个原型对象,通过对原型对象的复制,产生出更多同类型的对象(浅拷贝/深拷贝)

https://blog.csdn.net/TypantK/article/details/88708343 -- 3种浅拷贝/2种深拷贝

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值