Java23种设计模式
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

10 KiB

[JAVA]23种设计模式:工厂方法模式,抽象工厂模式,单例模式Ⅰ

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。

设计模式的分类

分为三大类:

  • 创造型模式(五种)

工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式

  • 结构型模式(七种)

适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式

  • 行为型模式(十一种)

策略模式,模板方法模式,观察者模式,迭代子模式,责任链模式,命令模式,备忘录模式,状态模式,访问者模式,中介者模式,解释器模式

补充:并发型模式,线程池模式

1、工厂方法模式(Factory Method)

工厂方法模式分为三种

1.1普通工厂方法模式

建立一个工厂类,对实现了同一接口的一些类进行实例的创建:

//共同接口
public interface Sender {
    public void Send();
}
public class CommonFactoryMailSender implements Sender{

    @Override
    public void Send() {
        System.out.println("This is MailSender!");
    }
    
}
public class CommonFactorySmsSender implements Sender{

    @Override
    public void Send() {
        System.out.println("This is Sms Sender!");
    }
    
}

建立工厂类:

public class CommonFactorySendFactory {
    public Sender producSender(String type){
        if("mail".equals(type)){
            return new CommonFactoryMailSender();
        }else if("sms".equals(type)){
            return new CommonFactorySmsSender();
        }else{
            System.out.println("类型错误!");
            return null;
        }
    }
}

进行使用:

public void testName() {
        CommonFactorySendFactory factorySendFactory= new CommonFactorySendFactory();
        Sender sender=factorySendFactory.producSender("mail");
        sender.Send();
        };
    }

1.2多个工厂方法模式

对普通工厂方法模式进行改进,提供多个工厂方法模式,分别创建对象

public class MultipleFactorySendFactory {
    public CommonFactoryMailSender produceSenderMail(){
        return new CommonFactoryMailSender();
    }
    public CommonFactorySmsSender produceSenderSms(){
        return new CommonFactorySmsSender();
    }
}

测试如下:

    public void test2(){
        MultipleFactorySendFactory factorySendFactory=new MultipleFactorySendFactory();
        Sender sender=factorySendFactory.produceSenderMail();
        sender.Send();
    }

1.3静态工厂方法模式

将多个工厂方法模式中的方法设置为静态,不需要创建实例,可以直接调用

public class StaticFactorySendFactory {
    public static CommonFactoryMailSender procduceSenderMail(){
        return new CommonFactoryMailSender();
    }
    public static CommonFactorySmsSender procduceSenderSms(){
        return new CommonFactorySmsSender();
    }
}

测试如下:

public void test3() {
    Sender sender=StaticFactorySendFactory.procduceSenderSms();
    sender.Send();
}

1.4总结

工厂方法模式适合大量需要创建,且具有共同的接口时,可以通过工厂方法模式进行创建 对于以上三种,第一种通过字符串判断来创建对应的对象,第三种相对于第二种不需要实例化工厂类,一般情况下使用静态工厂方法模式

2、抽象工厂模式(Abstract Factory)

工厂方法模式存在的问题是:类的创建依赖工厂类,如果想要扩展程序,就需要对工厂类进行修改,此时违背了闭包原则。从设计的角度,使用抽象工厂模式创建多个类,若需要新增功能直接增加新的工厂即可,同时无需修改之前的代码

public interface Sender {
    public void Send();
}

继承并实现方法:

//SmsSender
public class SmsSender implements Sender {

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

}
//MailSender
public class MailSender implements Sender {

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

}

实现共同接口:

public interface Sender {
    public void Send();
}

实现两个工厂类:

public class SendSmsFactory implements Provider{

    @Override
    public Sender produce() {
       return new SmsSender();
    }

}
public class SendMailFactory implements Provider{
    
    @Override
    public Sender produce(){
        return new MailSender();
    }
    
}

测试如下:

public void name() {
    Provider provider=new SendMailFactory();
    com.louzin.java23.abstractfactory.Sender sender=provider.produce();
    sender.Send();
}

2.1总结

若想添加一个新的功能,只需要做一个实现类实现Sender接口,同时创建一个工厂类实现Provider接口,无需改动现成的代码,扩展性较好

3、单例模式(Singleton)

单例对象(Singleton)是一种常见的设计模式。在Java的应用中,能保证在一个JVM中只存在一个该对象的实例,这样的好处是:

  • 限制某些繁琐类的建立,这类大型对象的创建是一笔很大的开销
  • 无需重复new,降低了系统内存的使用频率,减轻GC压力
  • 保证程序的核心流程只能创建一个
public class SingletonDemo1 {
    /* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
    private static SingletonDemo1 instance = null;

    /* 私有构造方法,防止被实例化 */
    private SingletonDemo1() {
    }

    /* 静态工程方法,创建实例 */
    public static SingletonDemo1 getInstance() {
        if (instance == null) {
            instance = new SingletonDemo1();
        }
        return instance;
    }

    /* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
    public Object readResolve() {
        return instance;
    }
}

这个类可以满足单例的基本要求,但是若将其置于多线程环境,将会出现问题,我们会对getInstance方法加synchronized关键字:

public static synchronized SingletonDemo1 getInstance() {
		if (instance == null) {
			instance = new SingletonDemo1();
		}
		return instance;
	}

但是synchronized锁住的是这个对象,每次调用的时候都要加锁,非常影响性能。实际上,只有第一次创建的时候才需加锁,之后就不需要了:

public static SingletonDemo1 getInstance() {
    if (instance == null) {
        synchronized (instance) {
            if (instance == null) {
                instance = new SingletonDemo1();
            }
        }
    }
    return instance;
}

这种方式很好理解,将加锁放在了内部,在调用的时候并不会加锁,只有在创建的时候才会加锁、 但是这种情况还是可能会出现问题

在Java指令中,创建对象赋值操作是分开进行的,也就是说instance = new SingletonDemo1()是分开进行的,但是JVM并不能保证这两个操作的先后顺序,也就可能会出现JVM先为新的SingletonDemo1实例分配空间,然后直接赋值给instance,在此之后才进行初始化SingletonDemo1实例,此时便会出错

以下为例:

  • A,B两个线程同时调用了这个类
  • A先判断完,进入synchronized块执行了instance = new SingletonDemo1()
  • JVM内部优化机制,先分配了空白内存,并完成赋值,此时还未初始化这个实例
  • B判断instance不是null后离开了synchronized块,并将结果返回给调用方法
  • 调用方法抛出异常

解决方式如下:

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

单例模式使用内部类来维护单例的实现,JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的,当我们第一次调用getInstance的时候,JVM能帮我们保证只会创建一次,并会把赋值给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 Object readResolve() {
        return instance;
    }
}

3.1影子实例

采用"影子实例"的方法为单例对象的属性同步更新

public class SingletonTest {
 
	private static SingletonTest instance = null;
	private Vector properties = null;
 
	public Vector getProperties() {
		return properties;
	}
 
	private SingletonTest() {
	}
 
	private static synchronized void syncInit() {
		if (instance == null) {
			instance = new SingletonTest();
		}
	}
 
	public static SingletonTest getInstance() {
		if (instance == null) {
			syncInit();
		}
		return instance;
	}
 
	public void updateProperties() {
		SingletonTest shadow = new SingletonTest();
		properties = shadow.getProperties();
	}
}
``` 4建造者模式Builder
工厂类模式服务于单个类而建造者模式将各个类集中起来管理用来创建复合对象实际上就是抽象工厂类和`Test`的结合
`Sender`接口两个实现类`MailSender``SmsSender`