一、概念:
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的代码设计经验的总结,是一种用于固定场合的固定套路
基本分类:
- 创建型模式:单例设计模式,工厂方法模式,抽象工厂模式
- 结构性模式:装饰器模式,代理模式
- 行为型模式:模板设计模式
二、设计模式详解:
- 单例设计模式:懒汉式,饿汉式。懒汉式需要对多线程进行同步处理
- 饿汉式:(立即加载)使用类的时候就创建实例
public class Singleton {
// 将自身实例化对象设置为一个属性,并用static、final修饰
private static final Singleton instance = new Singleton();
// 构造方法私有化
private Singleton() {}
// 静态方法返回该实例
public static Singleton getInstance() {
return instance;
}
}
优点:实现起来简单,没有多线程同步问题。
缺点:当类SingletonTest被加载的时候,会初始化static的instance,静态变量被创建并分配内存空间,从这以后,这个static的instance对象便一直占着这段内存(即便你还没有用到这个实例),当类被卸载时,静态变量被摧毁,并释放所占有的内存,因此在某些特定条件下会耗费内存。
- 懒汉式:(延迟加载)当调用get方法时,再创建实例
public class Singleton {
// 将自身实例化对象设置为一个属性,并用static修饰
private static Singleton instance;
// 构造方法私有化
private Singleton() {}
// 静态方法返回该实例
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
优点:实现起来比较简单,当类SingletonTest被加载的时候,静态变量static的instance未被创建并分配内存空间,当getInstance方法第一次被调用时,初始化instance变量,并分配内存,因此在某些特定条件下会节约了内存。
缺点:存在线程安全问题
- 线程安全的懒汉式:
public class Singleton {
// 将自身实例化对象设置为一个属性,并用static修饰
private static Singleton instance;
// 构造方法私有化
private Singleton() {}
// 静态方法返回该实例,加synchronized关键字实现同步
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
优点:在多线程的情况下,使用synchronized方法保证线程安全
缺点:效率低下
- 最优懒汉式:
public class Singleton {
// 将自身实例化对象设置为一个属性,并用static修饰
private static Singleton instance;
// 构造方法私有化
private Singleton() {}
// 静态方法返回该实例
public static Singleton getInstance() {
// 第一次检查instance是否被实例化出来,如果没有进入if块
if(instance == null) {
synchronized (Singleton.class) {
// 某个线程取得了类锁,实例化对象前第二次检查instance是否已经被实例化出来,如果没有,才最终实例出对象
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
-
普通工厂模式:建立一个工厂类,对实现了同一接口的不同实现类进行实例的创建
/**
* 发送邮件类
*/
public class MailSender implements Sender{
@Override
public void send() {
System.out.println("正在发送邮件...");
}
}
/**
* 发送短信类
*/
public class SmsSender implements Sender{
@Override
public void send() {
System.out.println("正在发送短信...");
}
}
/**
* 工厂类
*/
public class SendFactory {
// 自定义成员方法实现对象的创建
public Sender produce(String type) {
if ("mail".equals(type)) {
return new MailSender();
}
if ("sms".equals(type)) {
return new SmsSender();
}
return null;
}
}
/**
* 测试类
*/
public class SendFactoryTest {
public static void main(String[] args) {
// 1. 声明工厂类型的引用指向工厂类型的对象
SendFactory sf = new SendFactory();
// 2. 调用生产方法来实现对象的创建
Sender sender = sf.produce("mail");
// 3. 使用对象调用方法模拟发生的行为
sender.send();
}
}
主要缺点:在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,并可能出现空指针异常
优点:在创建大量对象的前提下,扩展性和可维护性强
-
多个工厂模式:
/**
* 工厂类
*/
public class SendFactory {
// 自定义成员方法实现对象的创建
public Sender produce(String type) {
if ("mail".equals(type)) {
return new MailSender();
}
if ("sms".equals(type)) {
return new SmsSender();
}
return null;
}
// 自定义对应的成员方法创建对应的对象
public Sender produceMail() {
return new MailSender();
}
public Sender produceSms() {
return new SmsSender();
}
}
/**
* 测试类
*/
public class SendFactoryTest {
public static void main(String[] args) {
// 1. 声明工厂类型的引用指向工厂类型的对象
SendFactory sf = new SendFactory();
// 2. 调用生产方法来实现对象的创建
//Sender sender = sf.produce("mail");
Sender sender = sf.produceMail();
// 3. 使用对象调用方法模拟发生的行为
sender.send();
}
}
主要缺点:在多个工厂方法模式中,为了能够正确创建对象,先需要创建工厂类的对象才能调用工厂类中的生产方法
-
静态工厂模式:
/**
* 工厂类
*/
public class SendFactory {
// 自定义成员方法实现对象的创建
public Sender produce(String type) {
if ("mail".equals(type)) {
return new MailSender();
}
if ("sms".equals(type)) {
return new SmsSender();
}
return null;
}
// 自定义对应的静态成员方法创建对应的对象
public static Sender produceMail() {
return new MailSender();
}
public static Sender produceSms() {
return new SmsSender();
}
}
/**
* 测试类
*/
public class SendFactoryTest {
public static void main(String[] args) {
// 1. 声明工厂类型的引用指向工厂类型的对象
//SendFactory sf = new SendFactory();
// 2. 调用生产方法来实现对象的创建
//Sender sender = sf.produce("mail");
//Sender sender = sf.produceMail();
Sender sender = SendFactory.produceMail();
// 3. 使用对象调用方法模拟发生的行为
sender.send();
}
}
实际意义:适合于出现了大量的产品需要创建且它们具有共同的接口时,可以通过工厂方法模式进行创建
主要缺点:工厂方法模式有一个问题就是,类的创建依赖工厂类,如果想要拓展程序生产新的产品,就必须对工厂的代码进行修改,就违背了开闭原则
-
抽象工厂模式:
/**
* 工厂接口类
*/
public interface Provider {
// 自定义抽象方法描述产品的生产行为
Sender produce();
}
/**
* 发送邮件的工厂类
*/
public class MailSendFactory implements Provider{
@Override
public Sender produce() {
return new MailSender();
}
}
/**
* 发送短信的工厂类
*/
public class SmsSendFactory implements Provider{
@Override
public Sender produce() {
return new SmsSender();
}
}
/**
* 测试类
*/
public class SendFactoryTest {
public static void main(String[] args) {
Provider provider = new MailSendFactory();
Sender sender = provider.produce();
sender.send();
}
}
优点:当需要生产新的产品时,只需要创建对应的工厂类即可
-
装饰器模式:给一个对象动态地增加一些新功能,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例
/**
* 定义接口
*/
public interface Sourceable {
// 自定义抽象方法
void method();
}
/**
* 定义原本的类
*/
public class Source implements Sourceable{
@Override
public void method() {
System.out.println("原本的类!");
}
}
/**
* 装饰类
*/
public class Decorator implements Sourceable{
private Sourceable source;
public Decorator(Sourceable source) {
this.source = source;
}
@Override
public void method() {
// 保证原有功能
source.method();
// 添加新的功能
System.out.println("我是装饰器!");
}
}
/**
* 测试类
*/
public class SourceableTest {
public static void main(String[] args) {
// 使用原有类实现功能
Sourceable sourceable = new Source();
sourceable.method();
System.out.println("------------------------------");
// 使用装饰类实现功能
Sourceable sourceable1 = new Decorator(sourceable);
sourceable1.method();
}
}
实际意义:
- 可以实现一个类功能的扩展
- 可以动态地增加功能,而且还能动态撤销
缺点:产生过多相似的对象,不易排错
-
代理模式:找一个代理类,替原对象进行一些操作
/**
* 代理类
*/
public class Proxy implements Sourceable{
private Source source;
public Proxy() {
source = new Source();
}
@Override
public void method() {
source.method();
System.out.println("我是代理模式!");
}
}
/**
* 测试类
*/
public class SourceableTest {
public static void main(String[] args) {
// 使用原有类实现功能
Sourceable sourceable = new Source();
sourceable.method();
System.out.println("------------------------------");
// 使用代理类实现功能
Sourceable sourceable1 = new Proxy();
sourceable1.method();
}
}
实际意义:
- 如果在使用的时候需要对原有的方法进行改进,可以采用一个代理类调用原有方法,并且对产生的结果进行控制
- 使用代理模式,可以将功能划分得更加清晰,有助于后期维护
与装饰器模式的比较:
装饰器模式通常的做法是将原始对象作为一个参数传给装饰者的构造器,而代理模式通常在一个代理类中创建一个被代理类的对象
装饰器模式关注于在一个对象上动态的添加方法,而代理模式关注于控制对对象的访问
-
模板模式:指一个抽象类中封装了一个固定流程,流程中的具体步骤可以由不同子类进行不同的实现,通过抽象类让固定的流程产生不同的结果
/**
* 定义一个抽象类
*/
public abstract class AbstractCalculator {
// 自定义成员方法实现将参数指定的表达式按照参数指定的规则进行切割并返回计算结果
public int splitExpression(String exp, String op) {
String[] sArr = exp.split(op);
return calculate(Integer.parseInt(sArr[0]), Integer.parseInt(sArr[1]));
}
// 自定义抽象方法实现运算
public abstract int calculate(int ia, int ib);
}
/**
* 定义加法
*/
public class Plus extends AbstractCalculator{
@Override
public int calculate(int ia, int ib) {
return ia + ib;
}
}
/**
* 定义减法
*/
public class Minus extends AbstractCalculator{
@Override
public int calculate(int ia, int ib) {
return ia - ib;
}
}
/**
* 测试类
*/
public class AbstractCalculatorTest {
public static void main(String[] args) {
AbstractCalculator abstractCalculator = new Plus();
int res = abstractCalculator.splitExpression("1+1", "\\+");
System.out.println("最终的运算结果是:" + res);
}
}
实际意义:
- 将多个子类共有且逻辑基本相同的内容提取出来实现代码复用
- 不同的子类实现不同的效果形成多态,有助于后期维护