设计模式之创建型模式(共五种)
一、概述
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节,对象的创建由相关的工厂来完成。就像我们去商场购买商品时,不需要知道商品是怎么生产出来一样,因为它们由专门的厂商生产。
创建型模式分为以下几种。
- 单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
- 原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
- 工厂方法(FactoryMethod)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。
- 抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
- 建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。
以上 5 种创建型模式,除了 工厂方法模式 属于类创建型模式,其他的全部属于对象创建型模式,我们将在之后的教程中详细地介绍它们的特点、结构与应用。
二、单例模式
2.1 概念
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
个人总结:在类内部创造单一对象,通过设置构造方法权限,使类外部无法再创造对象
注:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例
2.2 创建方式
主要使用懒汉和懒汉式
1、饿汉式:类初始化时,会立即加载该对象,线程天生安全,调用效率高。
2、懒汉式:类初始化时,不会初始化该对象,真正需要使用的时候才会创建该对象,具备懒加载功能。
3、静态内部方式:结合了懒汉式和饿汉式各自的优点,真正需要对象的时候才会加载,加载类是线程安全的。
4、枚举单例:使用枚举实现单例模式 优点:实现简单、调用效率高,枚举本身就是单例,由jvm从根本上提供保障!避免通过反射和反序列化的漏洞,缺点没有延迟加载。
5、双重检测锁方式 (因为JVM本质重排序的原因,可能会初始化多次,不推荐使用)
2.3 示例解析
1.饿汉式
该模式的特点是类一旦加载就创建一个单例,保证在调用getInstance方法之前单例已经存在了。饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题。
public class HungrySingleton {
//类初始化时,会立即加载该对象,线程安全,调用效率高
private static final HungrySingleton instance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return instance;
}
}
2.懒汉式
该模式的特点是类加载时没有生成单例,只有当第一次调用getlnstance方法时才去创建这个单例。
public class LazySingleton {
private LazySingleton() {
}//1.private 避免类在外部被实例化
private static volatile LazySingleton instance = null;//2.保证instance在所有线程中同步
public static LazySingleton getInstance() {//3
if (instance == null) {//4第一次检查
synchronized (LazySingleton.class) {//5加锁
if (instance == null) {//6第二次检查
instance = new LazySingleton();
}
}
}
return instance;
}
}
2.4 懒汉式和饿汉式区别
1、线程安全
饿汉式天生就是线程安全的,可以直接用于多线程。
懒汉式本身就非线程安全的,但是可以通过关键字volatile和 synchronized来实现线程安全。
2、资源加载和性能
饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成。
而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。
2.5 优缺点
优点:
1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。
2、避免对资源的多重占用(比如写文件操作)。
缺点:
没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
2.6 使用场景
1、要求生产唯一序列号。
2、WEB中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
3、创建的一个对象需要消耗的资源过多,比如I/O与数据库的连接等。注意事项:getInstance()
方法中需要使用同步锁,synchronized (Singleton.class)防止多线程同时进入造成instance被多次实例化。
注意:如果编写的是多线程程序,则不要删除上例代码中的关键字
volatile和synchronized,否则将存在线程非安全的问题。如果不删除这两个关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。单例模式中volatile关键字的作用
2.7 拓展
单例模式可扩展为有限的多例(Multitcm)模式,这种模式可生成有限个实例并保存在ArrayList中,客户需要时可随机获取
三、工厂模式
3.1 概念
它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。实现了创建者和调用者分离,工厂模式分为简单工厂、工厂方法、抽象工厂模式。
3.2 好处
1、工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。
2、利用工厂模式可以降低程序的耦合性,为后期的维护修改提供了很大的便利。
3、将选择实现类、创建对象统一管理和控制。从而将调用者跟我们的实现类解耦。
3.3 Spring开发中的工厂设计模式
1.Spring IOC
- 看过Spring源码就知道,在Spring IOC容器创建bean的过程是使用了工厂设计模式
- Spring中无论是通过xml配置还是通过配置类还是注解进行创建bean,大部分都是通过简单工厂来进行创建的。
- 当容器拿到了beanName和class类型后,动态的通过反射创建具体的某个对象,最后将创建的对象放到Map中。
2.为什么Spring IOC要使用工厂设计模式创建Bean呢
- 在实际开发中,如果我们A对象调用B,B调用C,C调用D的话我们程序的耦合性就会变高。(耦合大致分为类与类之间的依赖,方法与方法之间的依赖。)
- 在很久以前的三层架构编程时,都是控制层调用业务层,业务层调用数据访问层时,都是是直接new对象,耦合性大大提升,代码重复量很高,对象满天飞
- 为了避免这种情况,Spring使用工厂模式编程,写一个工厂,由工厂创建Bean,以后我们如果要对象就直接管工厂要就可以,剩下的事情不归我们管了。Spring IOC容器的工厂中有个静态的Map集合,是为了让工厂符合单例设计模式,即每个对象只生产一次,生产出对象后就存入到Map集合中,保证了实例不会重复影响程序效率。
3.4 工厂模式分类
工厂模式分为简单工厂、工厂方法、抽象工厂模式
- 简单工厂:用来生产同一等级结构中的任意产品。(不支持拓展增加产品)
- 工厂方法:用来生产同一等级结构中的固定产品。(支持拓展增加产品)
- 抽象工厂:用来生产不同产品族的全部产品。(不支持拓展增加产品;支持增加产品族)
3.5 简单工厂模式
简单工厂模式相当于是一个工厂中有各种产品,创建在一个类中,客户无需知道具体产品的名称,只需要知道产品类所对应的参数即可。但是工厂的职责过重,而且当类型过多时不利于系统的扩展维护。
public class Simple_Factory {
public static void main(String[] args) {
Car audi = CarFactory.createCar("audi");
Car bmw = CarFactory.createCar("bmw");
aodi.run();
bmw.run();
}
}
interface Car { //汽车
void run();
}
class Bmw implements Car {
@Override
public void run() {
System.out.println("this is bmw");
}
}
class AuDi implements Car {
public void run() {
System.out.println("this is audi");
}
}
class CarFactory {
public static Car createCar(String name) {
if (name.equalsIgnoreCase("audi")) {
return new AoDi();
}
if (name.equalsIgnoreCase("bmw")) {
return new Bmw();
}
return null;
}
}
- 优点/缺点
优点:简单工厂模式能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。明确区分了各自的职责和权力,有利于整个软件体系结构的优化。
缺点:很明显工厂类集中了所有实例的创建逻辑,容易违反GRASPR的高内聚的责任分配原则
3.6 工厂方法模式
工厂方法模式Factory Method,又称多态性工厂模式。在工厂方法模式中,核心的工厂类不再负责所有的产品的创建,而是将具体创建的工作交给子类去做。该核心类成为一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。
public class Factory_Method {
public static void main(String[] args) {
Car aodi = new AoDiFactory().createCar();
Car bmw = new BmwFactory().createCar();
aodi.run();
bmw.run();
}
}
//创建工厂
interface Car {
void run();
}
//创建工厂方法调用接口(所有的产品需要new出来必须继承他来实现方法)
interface CarFactory {
Car createCar();
}
//创建工厂的产品
class AoDi implements Car {
@Override
public void run() {
System.out.println("this is aodi");
}
}
class Bmw implements Car {
@Override
public void run() {
System.out.println("this is bmw");
}
}
//创建工厂方法调用接口的实例
class AoDiFactory implements CarFactory {
public Car createCar() {
return new AoDi();
}
}
class BmwFactory implements CarFactory {
public Car createCar() {
return new Bmw();
}
}
四、抽象工厂模式
4.1 概念
抽象工厂简单地说是工厂的工厂,抽象工厂可以创建具体工厂,由具体工厂来产生具体产品。
4.2 示例
1.创建第一个子工厂
//汽车
interface Car {
void run();
}
class CarA implements Car {
public void run() {
System.out.println("宝马");
}
}
class CarB implements Car {
public void run() {
System.out.println("摩拜");
}
}
2.创建第二个子工厂
//发动机
interface Engine {
void run();
}
class EngineA implements Engine {
public void run() {
System.out.println("转的快!");
}
}
class EngineB implements Engine {
public void run() {
System.out.println("转的慢!");
}
}
//3.创建一个总工厂
interface TotalFactory {
// 创建汽车
Car createChair();
// 创建发动机
Engine createEngine();
}
//总工厂实现类,由他决定调用哪个工厂的那个实例
class TotalFactoryReally implements TotalFactory {
public Engine createEngine() {
return new EngineA();
}
public Car createChair() {
return new CarA();
}
}
4.测试
public class Abstract_Factory {
public static void main(String[] args) {
TotalFactory totalFactory2 = new TotalFactoryReally();
Car car = totalFactory2.createChair();
car.run();
TotalFactory totalFactory = new TotalFactoryReally();
Engine engine = totalFactory.createEngine();
engine.run();
}
}
五、建造者模式
5.1 概念
将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
个人总结:将一些不会变的基本组件,通过builder,组合,构建复杂对象,实现分离
5.2 示例
public class BuilderDemo {
public static void main(String[] args) {
PriceBuilder priceBuilder = new PriceBuilder();
System.out.println("Car1和Car2:" + priceBuilder.Car1AndCar2());
System.out.println("Car1和Bus:" + priceBuilder.Car1AndBus());
}
}
//基本组件
interface Car {
}
//基本组件1
class Car1 implements Car {
int price = 20;
}
//基本组件2
class Car2 implements Car {
int price = 90;
}
//基本组件3
class Bus {
int price = 500;
}
class PriceBuilder {
//car1和car2的总价格
public int Car1AndCar2() {
int priceOfCar1 = new Car1().price;
int priceOfCar2 = new Car2().price;
return priceOfCar1 + priceOfCar2;
}
//car1和bus的总价格
public int Car1AndBus() {
int priceOfCar1 = new Car1().price;
int priceOfBus = new Bus().price;
return priceOfCar1 + priceOfBus;
}
}
六、原型模式
6.1 概念
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
个人总结:将对象复制了一份并返还给调用者,对象需继承Cloneable并重写clone()方法
6.2 示例
public class Prototype implements Cloneable {
private String message = "hello";
public Object clone() throws CloneNotSupportedException {
Prototype proto = (Prototype) super.clone();
//操作克隆对象
proto.message += " world!";
return proto;
}
public static void main(String[] args) throws CloneNotSupportedException {
Prototype p = (Prototype) new Prototype().clone();
//操作克隆对象
System.out.println(p.message);
}
}

浙公网安备 33010602011771号