常用设计模式(单例、工厂、适配器)
1、单例模式 (√)
单例模式是Java设计模式中最简单的设计模式之一,是属于创建型设计模式。所谓的单例模式,目的就是使设计单例模式的类,只会创建一个对象,并且这个对象也会提供一个全局访问的方法,进行访问。
单例模式的特点:
-
单例类只能有一个实例。
-
单例类的对象只能由自己创建自己的唯一对象。
-
单例类必须要给其他所有的类提供这一个唯一的对象。
-
单例类中的构造函数必须是私有的,就是为了防止出现多次创建。
单例模式的具体实现有以下几种方式:
1、饿汉式(单例实现)
/** * 饿汉式单例实现 */ public class SimpleObjectMyApplication { // 构造私有 private SimpleObjectMyApplication() {} // 上来就创建实例 private static SimpleObjectMyApplication instance = new SimpleObjectMyApplication(); // 设置一个公共的方法,用于访问这个变量 public static SimpleObjectMyApplication getInstance() { return instance; } }
特点:
-
线程安全:是线程安全的,因为他一上来就去创建实例,并且这个实例用static关键字修饰,在类加载的时候就已经初始化好了,并且只会初始化一次。
-
不能创建对象,构造函数私有化,就只能通过静态的函数进行调用公共方法。那在静态方法中就只能调用静态的成员,不能调用非静态成员,所以刚好在这种情况下,自身形成了线程安全。
/** * 懒汉式单例实现(懒汉式是属于调用的时候才去创建实例) */ public class SimpleObjectMyApplication { // 构造私有 private SimpleObjectMyApplication() {} // 声明变量 private static SimpleObjectMyApplication instance; // 设置一个公共的方法,用于访问这个变量 public static SimpleObjectMyApplication getInstance() { if (instance == null) { return new SimpleObjectMyApplication(); } return instance; } }从上面的代码可以发现:为了实现只创建一个单例对象,就使用:
if (instance == null) { return new SimpleObjectMyApplication(); }if语句判断当前的对象实例是不是为空,若为空就创建一个,不为空就返回已经创建好了的。但是这样会发生一个问题,在多线程中调用单例类,会出现线程不安全。所以代码修改如下:
/** * 懒汉式单例实现(懒汉式是属于调用的时候才去创建实例) */ public class SimpleObjectMyApplication { // 构造私有 private SimpleObjectMyApplication() {} // 声明变量 private static SimpleObjectMyApplication instance; // 设置一个公共的方法,用于访问这个变量 // 添加 synchronized 关键字 public static synchronized SimpleObjectMyApplication getInstance() { if (instance == null) { return new SimpleObjectMyApplication(); } return instance; } }3、双重检测机制 (线程安全,在多线程环境下,性能还是不错的)
/** * 双重检测单例实现(懒汉式是属于调用的时候才去创建实例) */ public class SimpleObjectMyApplication { // 构造私有 private SimpleObjectMyApplication() {} // 声明变量 private static SimpleObjectMyApplication instance; // 设置一个公共的方法,用于访问这个变量 public static synchronized SimpleObjectMyApplication getInstance() { if (instance == null) { synchronized (SimpleObjectMyApplication.class) { if (instance == null) { instance = new SimpleObjectMyApplication(); } } } return instance; } }
4、静态内部类 这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程,它跟第 1 种方式不同的是: 第 1 种方式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。 因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。
/** * 静态内部类 */ public class SimpleObjectMyApplication { // 构造私有 private SimpleObjectMyApplication() {} private static class SimpleObjectInner { private static final SimpleObjectMyApplication instance = new SimpleObjectMyApplication(); } public static final SimpleObjectMyApplication getInstance() { return SimpleObjectInner.instance; } }5、枚举
这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
不能通过 reflection attack 来调用私有构造方法。
1、饿汉式是线程安全、调用效率高、但是不支持延时加载。
2、懒汉式是线程不安全、调用效率也不高,但是支持延时加载。
3、静态内部类是线程安全、调用效率高,并且还支持延时加载。
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
不能通过 reflection attack 来调用私有构造方法。
public enum SimpleObjectApplication { INSTACE; public void function() { } }总的来说:
1、饿汉式是线程安全、调用效率高、但是不支持延时加载。
2、懒汉式是线程不安全、调用效率也不高,但是支持延时加载。
3、静态内部类是线程安全、调用效率高,并且还支持延时加载。
2、工厂模式 (√)
工厂模式的实现主要是为了使创建者与调用者进行分离。如果不使用工厂模式创建对象和调用对象的话,就会出现,调用者需要知道很多的额创建者的信息,整体的结构复杂,代码的耦合度也是很高的。
工厂模式包括三种实现:
1、简单工厂模式
2、工厂模式
3、抽象工厂模式
首先,了解简单工厂模式:
简单工厂模式又叫做:静态工厂模式。它是通过在创建类中实现添加静态方法,每一个静态方法都对应一个具体的创建者,进行创建并返回,调用者只需要在调用中通过该类的静态方法实现创建实体类。
但是,也有一个缺点就是:不能做到对扩展开放,对修改关闭,必须是在扩展的时候,是要修改代码的。
不使用工厂模式的示例:
public interface Car { void run(); }
public class BYD implements Car{ @Override public void run() { System.out.println("比亚迪在跑步"); } }
public class WL implements Car{ @Override public void run() { System.out.println("五菱在飙车"); } }
public class Main { public static void main(String[] args) { Car car1 = new BYD(); Car car2 = new WL(); car1.run(); car2.run(); } }关系图如下:
使用简单工厂模式,示例如下:
public interface Car { void run(); }
public class BYD implements Car{ @Override public void run() { System.out.println("比亚迪在跑步"); } }
public class WL implements Car{ @Override public void run() { System.out.println("五菱在飙车"); } }
public class SimpleFactory { public static WL createWL() { return new WL(); } public static BYD createBYD() { return new BYD(); } }
public class Main { public static void main(String[] args) { Car car1 = SimpleFactory.createWL(); Car car2 = SimpleFactory.createBYD(); car1.run(); car2.run(); } }关系图如下:
结构虽然看起来比之前的复杂了,但是对于调用者来说,就很轻松,不管以后的结构多复杂,调用者只需要知道Car接口和SimpleFactory简单工厂类。
工厂方法模式:工厂方法模式的出现是为了解决简单工厂模式不能对修改关闭,修改的时候必须要修改源代码,否则无法实现扩展。
工厂模式之所以能实现对扩展开放,对修改关闭,是因为,他会对每一个类都创建一个工厂,该工厂只会为该类使用,每个类的创建是不会相互影响的,但是这样也就会造成一个缺点:就是创建的类很多。
具体事例如下:
public interface Car { void run(); }
public interface CarFactory { Car createCar(); }
public class BYD implements Car { @Override public void run() { System.out.println("比亚迪在跑步"); } }
public class BYDFactory implements CarFactory { @Override public Car createCar() { return new BYD(); } }
public class WL implements Car { @Override public void run() { System.out.println("五菱在飙车"); } }
public class WLFactory implements CarFactory { @Override public Car createCar() { return new WL(); } }
public class Main { public static void main(String[] args) { Car c1 = new BYDFactory().createCar(); Car c2 = new WLFactory().createCar(); c1.run(); c2.run(); } }整个的代码结构:
后期想要实现扩展,就很简单了,只需要创建一个实体类,该实体类实现Car接口方法,再创建一个对应的工厂方法,使之能够去创建该实体类。
结构关系图:
简单工厂模式与工厂模式的区别:
1、从代码和功能的扩展上来看,都能扩展功能,但是简单工厂是需要修改源代码的,不支持开闭原则,但是工厂模式是支持开闭原则的。
2、从结构的复杂度上看,简单工厂模式的结构比较简单,而工厂模式涉及的类很多,之所以能实现开闭原则,就是去创建更多的类实现。
3、从使用上来看,使用的最多的还是简单工厂模式,涉及的类少,结构不复杂,但是从设计原理来看,是需要使用工厂模式的。
抽象工厂模式:
抽象工厂是工厂模式的升级版本,不管是简单工厂模式还是工厂模式,都是生产某一个产品,但是呢,现在抽象工厂模式可以生产一个产品族,比如合一生产一套高端的物件,可以生产一套低端的产品,抽象工厂不能生成一个产品。
具体事例如下:
public interface Seat { void massage(); } class LuxurySeat implements Seat { @Override public void massage() { System.out.println("高端座椅--可以按摩"); } } class LowSeat implements Seat { @Override public void massage() { System.out.println("低端座椅--不能按摩"); } }
public interface Engine { void run(); void start(); } class LuxuryEngine implements Engine { @Override public void run() { System.out.println("高端引擎--跑的块"); } @Override public void start() { System.out.println("高端引擎--启动的块"); } } class LowEngine implements Engine { @Override public void run() { System.out.println("低端引擎--跑的慢"); } @Override public void start() { System.out.println("低端引擎--启动的慢"); } }
public interface CarFactory { Engine createEngine(); Seat createSeat(); }
public class LowFactory implements CarFactory{ @Override public Engine createEngine() { return new LowEngine(); } @Override public Seat createSeat() { return new LowSeat(); } }
public class LuxuryFactory implements CarFactory{ @Override public Engine createEngine() { return new LuxuryEngine(); } @Override public Seat createSeat() { return new LuxurySeat(); } }
public class Client { public static void main(String[] args) { CarFactory carFactory = new LuxuryFactory(); Engine engine = carFactory.createEngine(); engine.run(); engine.start(); CarFactory carFactory1 = new LowFactory(); Seat seat = carFactory1.createSeat(); seat.massage(); } }具体结果:
工厂模式的总结:
1、简单工厂模式,也叫静态工厂模式
1、简单工厂模式,也叫静态工厂模式
虽然某种程度上不符合设计原则,但是实际是用最多。
2、工厂方法模式
不修改已有类的前提下,通过添加新的工厂类来实现开闭原则,进行扩展。
3、抽象工厂模式
不可以增加产品,但是可以增加产品族!
3、适配器模式 (√)
什么是适配器模式?适配器模式就是将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容,而不能进行正常工作的,适配后能够正常工作。
在适配器模式中,包含三种角色:
第一、目标接口(Target):客户所期待的适配之后的接口。
第二、需要被适配的类(Adaptee):需要适配的类。
第三、适配器(Adapter):可以通过包装一个被适配的对象,将原接口转换成被适配的接口。
具体示例如下:
/** * 需要被适配的类 */ public class Adaptee { public void run() { System.out.println("被适配的类运行"); } }
/** * 桥梁接口 */ public interface Target { void TargetRun(); }
/** * 桥梁实现类 */ public class Adapter extends Adaptee implements Target { //这个桥梁需要把被适配的类,组合进来形成组合关系,才能进行适配 @Override public void TargetRun() { super.run(); } }
/** * 客户端类 * 客户端是通过适配器来进行与被适配的类实现联系的 */ public class Client { // 想要实现在客户端和被支配的类进行联系,首先把适配器的接口,组合进来 // 再加上适配器接口,又组合了被适配的类,所以,可以达到适配目的 public void TargetClient(Target target) { target.TargetRun(); } public static void main(String[] args) { Client client = new Client(); Adaptee adaptee = new Adaptee(); Target target = new Adapter(); client.TargetClient(target); } }