创建型模式
目录
☆多个方法工厂(没有字符串参数,调用不同方法作为传递参数方式 --> 工厂多个方法)
ConcurrentBuilder:每个套餐应该说明(实现)套餐的每个内容是什么
测试:拿到菜单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种深拷贝