设计模式 — 创建型模式
简介
创建型模式,就是创建对象的模式,抽象了实例化的过程。它帮助一个系统独立于如何创建、组合和表示它的那些对象。关注的是对象的创建,创建型模式将创建对象的过程进行了抽象,也可以理解为将创建对象的过程进行了封装,作为客户程序仅仅需要去使用对象,而不再关心创建对象过程中的逻辑
单例模式
定义:确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
类图:
主要有三个部分:
私有的构造方法 指向自己实例的私有静态引用 以自己实例为返回值的静态的公有的方法
特点:*
优点:
在内存中只有一个对象,节省内存空间。 避免频繁的创建销毁对象,可以提高性能。 避免对共享资源的多重占用。 可以全局访问。
适用场景:
需要频繁实例化然后销毁的对象。 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。 有状态的工具类对象。 频繁访问数据库或文件的对象等等。
注意事项:
只能使用单例类提供的方法得到单例对象,不要使用反射,否则将会实例化一个新对象。 不要做断开单例类对象与类中静态引用的危险操作。 多线程使用单例使用共享资源时,注意线程安全问题。
饿汉式:
/** * 一:饿汉式(类加载的时候就创建了实例) * 优点:实现了线程安全,编写简单;执行效率高 * 缺点:初始化就创建了实例,对加载速度和内存有消耗,,所以这种方法要求单例对象初始化速度快且占用内存小 */ // 1、成员变量静态化 private static Singleton3 instance = new Singleton3(); //2、构造函数私有化,防止外部实例化 private Singleton3(){} //3、提供公共静态创建实例方法 public static Singleton3 getInstance(){ return instance; } /** * 二:枚举类型:根据枚举类型的特点,实现单例模式所需要的创建单例、线程安全、简洁的需求 * 优点:实现比较简洁,保证了单实例,线程安全 * 缺点:使用枚举类型,不是很熟悉 */ /** * 枚举类型的特点: * 1、枚举类型 = 不可被继承的类(final): * 枚举本质上是通过普通类实现的,只是编译器为我们进行了特殊处理 * 每个枚举类型都继承自java.lang.Enum,并自动添加了values(),valueOf() * 枚举类的实例 = 常量 * 2、每个枚举元素 = 类静态常量 = 1个实例 * 枚举元素,都是通过静态代码来进行初始化,即在类加载期间进行初始化,保证了实例只被创建一次,线程安全 * 获取枚举元素 = 获取实例 * 3、构造方法 访问权限 默认=私有(private):他的构造器是私有的,底层没有可供调用的无参数的构造器;防止了其他人创建实例 * 4、每一个枚举类型&枚举变量在JVM中都是唯一的: * 即java在序列化和反序列化美剧时做了特殊的规定:枚举的writeObject(),readObject(),readObjectNoData()等方法是被禁用的,因此不存在序列化接口之后调用readObject会破环单例的问题 * 保证了枚举元素的不可变行,即不能通过克隆、序列化&反序列化来复制枚举,即保证了1个枚举常量=1个实例 即单例 */ public enum Singleton { INSTANCE; public void whateverMethod() { } }
懒汉式:
//程序中创建类只有两种方式:1创建类的一个对象,用该对象去调用类中的方法;2使用类名直接调用类中方法,类名.方法名();单例模式防止外部new对象,只能使用类方法,且是静态的。 /** * 一:懒汉式(需要的时候创建实例),线程不安全。当多个线程同时访问时,不能正常工作。 * 优点:按需加载 * 缺点:线程不安全。多个线程同时访问时,会创建多个实例。 */ // 1、成员变量静态化 private static Singleton1 instance; //2、构造函数私有化,防止外部实例化 private Singleton1(){} //3、提供公共静态创建实例方法 public static Singleton1 getInstance() { if (instance == null) { instance = new Singleton1(); } return instance; } /** * 二:懒汉式:线程安全,加同步锁, * 优点:实现了线程安全 * 缺点:每次访问都要进行线程同步(调用synchronized锁),造成过多的同步开销(加锁=耗时、耗能) */ // 1、成员变量静态化 private static Singleton1 instance; //2、构造函数私有化,防止外部实例化 private Singleton1(){} //3、提供公共静态创建实例方法 public static synchronized Singleton1 getInstance() { if (instance == null) { instance = new Singleton1(); } return instance; } // 1、成员变量静态化 private static Singleton1 instance; //2、构造函数私有化,防止外部实例化 private Singleton1(){} //3、提供公共静态创建实例方法 public static Singleton1 getInstance() { synchronized(Singleton.class){ if (instance == null) { instance = new Singleton1(); } } return instance; } /** *三:懒汉式改进—双重校验锁;在同步锁的基础上,添加一层if判断,若单例已经创建,则不需要再执行加锁操作就可以获取实例,从而提高了性能。 *优点:避免了每次调用都要调用synchronized锁,同时双重校验又保证了线程安全,不会重复创建实例。 * 缺点:较为复杂,容易出错 */ // 1、成员变量静态化 private static Singleton1 instance = null; //2、构造函数私有化,防止外部实例化 private Singleton1(){} //3、提供公共静态创建实例方法 public static Singleton1 getInstance() { if (instance == null) { synchronized (Singleton1.class) { if (instance == null) { instance = new Singleton1(); } } } return instance; } /** * 四:静态内部类。在静态内部类中创建单例,在加载内部类时才会创建单例 * 优点:根据静态内部类的特性,实现了线程安全,按需加载,同时比较简洁 */ // 1、创建静态内部类 private static class Singleton2{ // 静态类里面创建单例 private static Singleton1 instance = new Singleton1(); } //2、构造函数私有化,防止外部实例化 private Singleton1(){} //3、提供公共静态创建实例方法 public static Singleton1 getInstance() { //调用静态类的实例方法 return Singleton2.instance; } /** * 调用过程说明 * 1、外部调用类的getInstance()方法 * 2、getInstance()方法自动调用内部类的方法,实现初始化 * 3、而该类在装载 & 被初始化时,会初始化他的静态域,从而创建单例 * 4、由于是静态域,因此JVM只会加载一遍,java虚拟机保证了线程安全 * 5、最终实现只创建一个实例。 */
简单工厂模式
定义:又静态工厂模式,可以根据参数的不同返回不同类的实例。
类图:
由此可以看出:简单工厂模式由三部分组成:具体工厂、具体产品和抽象产品
工厂类(Creator)角色:担任这个角色的是简单工厂模式的核心,含有与应用紧密相关的商业逻辑。工厂类在客户端的直接调用下创建产品对象,它往往由一个具体Java类实现。 抽象产品(AbstractProduct)角色:担任这个角色的类是由简单工厂模式所创建的对象的父类,或它们共同拥有的接口。抽象产品角色可以用一个Java接口或者Java抽象类实现。 具体产品(ConcreteProduct)角色:简单工厂模式所创建的任何对象都是这个角色的实例,具体产品角色由一个具体Java类实现。
总结:
简单工厂只有一个工厂类,通过switch语句来区分创建哪个产品。 工厂方法模式是扩展了简单工厂模式,区分为抽象工厂和具体工厂实现。
工厂方法模式
定义:定义一个创建对象的工厂接口,让子类决定实例化哪一个类,将实际的创建工作推迟到子类当中。
类图:
工厂方法模式包含
抽象产品:一般是产品接口或者抽象产品类。主要目的是定义产品的规范,所有的产品实现都必须遵循产品接口定义的规范。产品接口是调用者最为关心的,产品接口定义的优劣直接决定了调用者代码的稳定性。 具体产品:实现产品接口的具体类,决定了产品在客户端中的具体行为。 抽象工厂:一般是工厂接口或者抽象工厂类。是工厂方法模式的核心,与调用者直接交互用来提供产品。 具体工厂:在编程中,工厂实现决定如何实例化产品,是实现扩展的途径,需要有多少种产品,就需要有多少个具体的工厂实现。
优缺点:
优点:(1)封装了创建产品的方法,用户无须关心具体实现;(2)添加新产品时,只需要添加具体产品类和具体工厂类(实现或继承抽象产品、工厂类);(3)将创建工厂方法推迟到子类当中,实现了多态。工厂方法模式也叫多态工厂模式,之所以是多态,因为所有具体工厂类都具有同一父类。 缺点:(1)在添加新产品时,需要添加具体产品类、具体工厂类。当存在较多产品时,导致产品、产品工厂类增多,一定程度上增加了系统的复杂度,更多的类需要编译和运行,给系统带来了一些额外的开销。(2) 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。
代码举例
//1、抽象产品类 public interface Moveable { public void run(); } //2、具体产品实现类 public class Car implements Moveable { @Override public void run() { System.out.println("小汽车!"); } } public class Plane implements Moveable { @Override public void run() { System.out.println("大飞机!"); } } //3、抽象工厂类 public abstract class Factory { public abstract Moveable create(); } //4、具体工厂实现类 public class CarFactory extends Factory { @Override public Moveable create() { return new Car(); } } public class PlaneFactory extends Factory { @Override public Moveable create() { return new Plane(); } } //测试类 public class FactoryTest { @Test public void FactoryTest(){ Factory car = new CarFactory(); Moveable m = car.create(); // Factory plane = new PlaneFactory(); // Moveable m= plane.create(); m.run(); } }
抽象工厂模式
定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
类图:
可以看到,抽象工厂模式也是包含抽象产品、具体产品、抽象工厂、具体工厂。
但是抽象工厂中有产品等级结构和产品族的概念。
产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。 产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。
和工厂方法模式的区别:
工厂方法模式提供的是一个产品等级结构的实现,抽象工厂模式提供多个不同的产品等级结构的实现。
优缺点:
优点:可以在类的内部对产品族进行约束
缺点:产品族扩展比较麻烦。当增加新的产品时,所有的工厂类都要添加其相关代码。
代码举例:
//1、抽象产品类 public abstract class Vehicle{ public abstract void run(); } public abstract class Food { public abstract void printName(); } public abstract class Weapon { public abstract void shoot(); } //2、具体实现类 //第一个产品族 public class Car extends Vehicle { @Override public void run() { System.out.println("小汽车!"); } } public class Apple extends Food{ @Override public void printName() { System.out.println("红苹果"); } } public class AK47 extends Weapon { @Override public void shoot() { System.out.println("大火球,huhuhu。。。。。。"); } } //第2个产品族 public class Plane extends Vehicle { @Override public void run() { System.out.println("大灰机"); } } public class Pizza extends Food{ @Override public void printName() { System.out.println("披萨"); } } public class _98K extends Weapon{ @Override public void shoot() { System.out.println("砰砰砰。。。。。。"); } } //3、抽象工厂类 public abstract class AbstractFactory { public abstract Vehicle createVehicle(); public abstract Weapon createWeapon(); public abstract Food createFood(); } //4、具体工厂实现类 public class DefaultFactory extends AbstractFactory{ @Override public Vehicle createVehicle() { return new Car(); } @Override public Weapon createWeapon() { return new AK47(); } @Override public Food createFood() { return new Apple(); } } public class SuperFactory extends AbstractFactory{ @Override public Vehicle createVehicle() { return new Plane(); } @Override public Weapon createWeapon() { return new _98K(); } @Override public Food createFood() { return new Pizza(); } } //5、测试类 public class AbstractFactoryTest { @Test public void abstractFactoryTest(){ // AbstractFactory a = new DefaultFactory(); AbstractFactory a = new SuperFactory(); Vehicle v= a.createVehicle(); v.run(); Weapon w = a.createWeapon(); w.shoot(); Food f= a.createFood(); f.printName(); } }
总结:
建造者模式
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
类图:
建造者模式包含四个要素:
产品类:一般是一个较为复杂的对象,也就是说创建对象的过程比较复杂,一般会有比较多的代码量。在本类图中,产品类是一个具体的类,而非抽象类。实际编程中,产品类可以是由一个抽象类与它的不同实现组成,也可以是由多个抽象类与他们的实现组成。 抽象建造者:引入抽象建造者的目的,是为了将建造的具体过程交与它的子类来实现。这样更容易扩展。一般至少会有两个抽象方法,一个用来建造产品,一个是用来返回产品。 建造者:实现抽象类的所有未实现的方法,具体来说一般是两项任务:组建产品;返回组建好的产品。 导演类:负责调用适当的建造者来组建产品,导演类一般不与产品类发生依赖关系,与导演类直接交互的是建造者类。一般来说,导演类被用来封装程序中易变的部分。
优缺点:
优点:
将具体的业务逻辑实现封装在了导演类中,无须知道具体产品类的结构; 当添加新的产品时,只需要实现一个新的建造者类,并添加相应的导演类实现创建具体的产品。
代码实现:
class Product { private String name; private String type; public void showProduct(){ System.out.println("名称:"+name); System.out.println("型号:"+type); } public void setName(String name) { this.name = name; } public void setType(String type) { this.type = type; } } abstract class Builder { public abstract void setPart(String arg1, String arg2); public abstract Product getProduct(); } class ConcreteBuilder extends Builder { private Product product = new Product(); public Product getProduct() { return product; } public void setPart(String arg1, String arg2) { product.setName(arg1); product.setType(arg2); } } public class Director { private Builder builder = new ConcreteBuilder(); public Product getAProduct(){ builder.setPart("宝马汽车","X7"); return builder.getProduct(); } public Product getBProduct(){ builder.setPart("奥迪汽车","Q5"); return builder.getProduct(); } } public class Client { public static void main(String[] args){ Director director = new Director(); Product product1 = director.getAProduct(); product1.showProduct(); Product product2 = director.getBProduct(); product2.showProduct(); } }
与工厂模式的区别:
从结构上看,建造者模式只比工厂模式多了一个“导演类”的角色,如果没有这个导演类,就类似简单工厂模式了。
工厂模式是将对象的创建过程封装在工厂类中,由工厂类向客户端提供最终的产品。
建造者模式中,建造类一般只提供产品类中各个组件的建造,而将具体的建造过程交由导演类,由导演类实现具体的产品。
所以建造者模式一般用来创建更复杂的对象。
原型模式
待续。
参考资料
http://design-patterns.readth...
https://www.w3cschool.cn/java...