创建型模式
创建型模式的作用就是创建对象,说到创建一个对象,最熟悉的就是new一个对象,然后set相关属性。但是在很多场景下,我们需要给客户端提供更加友好的创建对象的方式,尤其是那种我们定义了类,但是需要提供给其他开发者使用的时候。
1、简单工厂模式
定义:定义一个工厂类,可以根据参数的不同返回不同类型的实例,被创建的实例都具有共同的父类
在简单工厂模式中用于被创建实例的方法通常为静态(static)方法,因此简单工厂模式又被称为静态工厂方法(Static Factory Method)。下面进行代码理解:
代码示例:
package com.creation_Pattern.p01_simple_factory;
//产品
public interface Product {
//价格
int getPrice();
//产品名
String getName();
}
代码示例:
package com.creation_Pattern.p01_simple_factory;
//产品A
public class ProductA implements Product{
@Override
public int getPrice() {
return 200;
}
@Override
public String getName() {
return "Product_A";
}
}
代码示例:
package com.creation_Pattern.p01_simple_factory;
//产品B
public class ProductB implements Product{
@Override
public int getPrice() {
return 200;
}
@Override
public String getName() {
return "Product_B";
}
}
按照正常创建对象我们会new ProductA,实现其方法,new ProductB,实现其方法,但是当我们建造一个工厂类
示例代码:
package com.creation_Pattern.p01_simple_factory;
/**
* @author: 皮先生kx
* @Description: 根据输入类型生成对应产品
* @date: created on 2021/10/17 16:24
* @email: 2472177078@qq.com
*/
public class Factory {
public static Product createProduct(String type){
Product product = null;
switch (type){
case "A":
product = new ProductA();
break;
case "B":
product = new ProductB();
break;
}
return product;
}
}
代码示例:
package com.creation_Pattern.p01_simple_factory;
public class Test {
public static void main(String[] args) {
//ProductA productA = new ProductA();
Product productA = Factory.createProduct("A");
System.out.println(productA.getName()+", "+productA.getPrice());
}
}
简单工厂模式的优点:
- 工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责。
- 客户端无需知道所创建具体产品的类名,只需要知道参数即可
- 也可以引入配置文件,在不修改客户端代码的情况下更好和添加新的具体产品类
简单工厂模式的缺点:
- 工厂类集中了所有产品的创建逻辑,职责过重,一旦异常,整个系统将受到影响
- 使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度
- 系统扩展很难,一旦增加新产品就必须修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂
- 简单工厂模式使用了static工厂方法,造成工厂角色无法形成基于继承的登记结构
简单工厂模式的使用场景:
- 工厂类复杂创建的对象较少,因为不会造成工厂方法中的业务逻辑过于发杂
- 客户端只知道传入工厂类的参数,对如何创建对象不关心
2、工厂方法模式
定义一个抽象工厂,其定义了产品的生产接口,但是不负责具体的产品,将生产任务交给不同的派生类工厂。这样工厂就不用通过指定类型来创建对象了。
代码示例:
package com.creation_Pattern.p02_factory_method;
//定义工厂接口
public interface IFactory {
Product createProduct();
}
代码示例:
package com.creation_Pattern.p02_factory_method;
//由FactoryA来实现工厂接口用于A产品的专项生产
public class FactoryA implements IFactory{
@Override
public Product createProduct() {
return new ProductA();
}
}
代码示例:
package com.creation_Pattern.p02_factory_method;
//由FactoryB来实现工厂接口用于B产品的专项生产
public class FactoryB implements IFactory{
@Override
public Product createProduct() {
return new ProductB();
}
}
代码示例:
package com.creation_Pattern.p02_factory_method;
/**
* @author: 皮先生kx
* @Description:
* @date: created on 2021/10/17 16:23
* @email: 2472177078@qq.com
*/
public class Test {
public static void main(String[] args) {
//选择工厂进行生产
FactoryA factoryA = new FactoryA();
Product productA = factoryA.createProduct();
System.out.println(productA.getName()+','+productA.getPrice());
}
}
当我们需要增加产品C的时候我们就可以创建一个FactoryC工厂C来实现IFactory接口,对原有的代码没有任何影响,比较符合我的开放封闭原则。
3、抽象工厂模式
对于上面两种模式不管工厂怎么拆分抽象,都只是针对一类产品,如果要生产另外一种产品,应该怎么表示呢?
最简单的方式是把工厂方式模式完全复制一份,用于生产新的产品。显然这是一个笨办法,并不利于扩展和维护。
代码示例:
package com.creation_Pattern.p03_abstract_factory;
//礼品接口
public interface Gift {
String getGiftName();
}
代码示例:
package com.creation_Pattern.p03_abstract_factory;
//礼品A
public class GiftA implements Gift{
@Override
public String getGiftName() {
return "Gift_A";
}
}
代码示例:
package com.creation_Pattern.p03_abstract_factory;
//礼品B
public class GiftB implements Gift{
@Override
public String getGiftName() {
return "Gift_B";
}
}
代码示例:
package com.creation_Pattern.p03_abstract_factory;
public interface IFactory {
Product createProduct();
Gift createGift();
}
代码示例:
package com.creation_Pattern.p03_abstract_factory;
public class FactoryA implements IFactory {
@Override
public Product createProduct() {
return new ProductA();
}
@Override
public Gift createGift() {
return new GiftA();
}
}
```java
代码示例:
package com.creation_Pattern.p03_abstract_factory;
public class FactoryB implements IFactory {
@Override
public Product createProduct() {
return new ProductB();
}
@Override
public Gift createGift() {
return new GiftB();
}
}
代码示例:
package com.creation_Pattern.p03_abstract_factory;
/**
* @author: 皮先生kx
* @Description:
* @date: created on 2021/10/17 16:23
* @email: 2472177078@qq.com
*/
public class Test {
public static void main(String[] args) {
FactoryA factoryA = new FactoryA();
Product productA = factoryA.createProduct();
Gift giftA = factoryA.createGift();
System.out.println(productA.getName()+','+productA.getPrice());
}
}
抽象工厂模式通过在AbstractFactory中增加创建新产品的接口,并在具体子工厂中实现新加产品的创建,当然前提是子工厂支持生产该产品。否则继承的这个接口可以什么也不干。
4、建造者模
定义:建造者模式是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
如何使用:用户只需要给出复杂对象的类型和内容,建造者模式复制按顺序创建复杂对象(把内部的建筑过程和细节隐藏起来)
使用场景:
- 方便用户创建复杂的对象(不需要知道实现过程)
- 代码复用性/封装性(将对象构建过程和细节进行复用/封装)
4.1 通过Client、Director、Builder和Product形成的建造者模式
1.角色
抽象建造者(builder):描述具体建造者的公共接口,一般用来定义建造细节的方法,并不涉及具体的对象部件的创建
具体建造者(ConcreteBuilder):描述具体建造者,并实现抽象建造者公共接口
指挥者(Director):调用具体建造者来创建复杂对象(产品)的各个部分,并按照一定顺序(流程)来创建发杂对象
产品(Product):描述一个由一系列部件组成较为复杂的对象
2.步骤
以建造房子为事例,假设造房子简化为如下步骤:地基—>钢筋工程—>铺电线---->粉刷
首先要找一个建筑公司(指挥者),建筑公司指挥工人(具体建造者)造房子(产品),最后验收:
1)创建抽象建造者定义造房子步骤
2)创建工人具体实现造房子步骤
3)创建承包商指挥工人施工
4)验收,检查是否建造完成
代码示例:
package com.creation_Pattern.p04_builder;
/**
* @author: 皮先生kx
* @Description: 产品房子
* @date: created on 2021/10/17 19:18
* @email: 2472177078@qq.com
*/
public class Product {
private String buildA;
private String buildB;
private String buildC;
private String buildD;
public String getBuildA() {
return buildA;
}
public void setBuildA(String buildA) {
this.buildA = buildA;
}
public String getBuildB() {
return buildB;
}
public void setBuildB(String buildB) {
this.buildB = buildB;
}
public String getBuildC() {
return buildC;
}
public void setBuildC(String buildC) {
this.buildC = buildC;
}
public String getBuildD() {
return buildD;
}
public void setBuildD(String buildD) {
this.buildD = buildD;
}
@Override
public String toString() {
return buildA + "\n" +buildB + "\n" +buildC + "\n" +buildD + "\n" +"房子验收完成";
}
}
代码示例:
package com.creation_Pattern.p04_builder;
/**
* @author: 皮先生kx
* @Description: 建造者抽象类
* @date: created on 2021/10/17 19:20
* @email: 2472177078@qq.com
*/
public abstract class Builder {
//地基
abstract void buildA();
//钢筋工程
abstract void buildB();
//铺电线
abstract void buildC();
//粉刷
abstract void buildD();
//完工验收
abstract Product getProduct();
}
代码示例:
package com.creation_Pattern.p04_builder;
/**
* @author: 皮先生kx
* @Description: 具体的建造者(建筑工人)
* @date: created on 2021/10/17 19:20
* @email: 2472177078@qq.com
*/
public class ConcreteBuilder extends Builder{
private Product product;
public ConcreteBuilder(){
product = new Product();
}
@Override
void buildA() {
product.setBuildA("地基");
}
@Override
void buildB() {
product.setBuildB("钢筋工程");
}
@Override
void buildC() {
product.setBuildC("铺电线");
}
@Override
void buildD() {
product.setBuildD("粉刷");
}
@Override
Product getProduct() {
return product;
}
}
代码示例:
package com.creation_Pattern.p04_builder;
/**
* @author: 皮先生kx
* @Description: 指挥者
* @date: created on 2021/10/17 19:20
* @email: 2472177078@qq.com
*/
public class Director {
//指挥工人按顺序造房子
public Product create(Builder builder){
builder.buildA();
builder.buildB();
builder.buildC();
builder.buildD();
return builder.getProduct();
}
}
代码示例:
package com.creation_Pattern.p04_builder;
/**
* @author: 皮先生kx
* @Description:
* @date: created on 2021/10/17 19:20
* @email: 2472177078@qq.com
*/
public class Test {
public static void main(String[] args) {
Director director = new Director();
Product create = director.create(new ConcreteBuilder());
System.out.println(create.toString());
}
}
运行结果:
"C:\Program Files\Java\jdk1.8.0\bin\java.exe"
地基
钢筋工程
铺电线
粉刷
房子验收完成
Process finished with exit code 0
4.2 通过静态的内部类方式实现零件无序装配化构造
1、角色
主要角色:抽象建造者、具体建造者、产品
比第一种少了指挥者,主要是因为第二种方式把指挥者交个用户来操作,使得产品创建根据简单灵活
2、步骤
1)创建建造者定义家具
2)创建实现家具
3)随意搭配家具
代码示例:
package com.creation_Pattern.p05_builder;
/**
* @author: 皮先生kx
* @Description: 家具
* @date: created on 2021/10/17 19:18
* @email: 2472177078@qq.com
*/
public class Product {
//理解为ABCD四样家具
private String buildA;
private String buildB;
private String buildC;
private String buildD;
public String getBuildA() {
return buildA;
}
public void setBuildA(String buildA) {
this.buildA = buildA;
}
public String getBuildB() {
return buildB;
}
public void setBuildB(String buildB) {
this.buildB = buildB;
}
public String getBuildC() {
return buildC;
}
public void setBuildC(String buildC) {
this.buildC = buildC;
}
public String getBuildD() {
return buildD;
}
public void setBuildD(String buildD) {
this.buildD = buildD;
}
@Override
public String toString() {
return buildA + "\n" +
buildB + "\n" +
buildC + "\n" +
buildD + "\n" +"家具创建完成";
}
}
代码示例:
package com.creation_Pattern.p05_builder;
/**
* @author: 皮先生kx
* @Description: 建造者
* @date: created on 2021/10/17 19:20
* @email: 2472177078@qq.com
*/
public abstract class Builder {
//地基
abstract Builder buildA(String msg);
//钢筋工程
abstract Builder buildB(String msg);
//铺电线
abstract Builder buildC(String msg);
//粉刷
abstract Builder buildD(String msg);
//完工验收
abstract Product build();
}
代码示例:
package com.creation_Pattern.p05_builder;
/**
* @author: 皮先生kx
* @Description: 具体的建造者(建筑工人)
* @date: created on 2021/10/17 19:20
* @email: 2472177078@qq.com
*/
public class ConcreteBuilder extends Builder {
private Product product;
public ConcreteBuilder(){
product = new Product();
}
@Override
Builder buildA(String msg) {
product.setBuildA(msg);
return this;
}
@Override
Builder buildB(String msg) {
product.setBuildB(msg);
return this;
}
@Override
Builder buildC(String msg) {
product.setBuildC(msg);
return this;
}
@Override
Builder buildD(String msg) {
product.setBuildD(msg);
return this;
}
@Override
Product build() {
return product;
}
}
代码示例:
package com.creation_Pattern.p05_builder;
/**
* @author: 皮先生kx
* @Description:
* @date: created on 2021/10/17 19:20
* @email: 2472177078@qq.com
*/
public class Test {
public static void main(String[] args) {
ConcreteBuilder concreteBuilder = new ConcreteBuilder();
Product build = concreteBuilder.buildA("床").buildB("衣柜").buildC("沙发").buildD("电视柜").build();
System.out.println(build.toString());
}
}
运行结果:
"C:\Program Files\Java\jdk1.8.0\bin\java.exe"
床
衣柜
沙发
电视柜
家具创建完成
Process finished with exit code 0
与工厂模式的区别:建造者模式根据关注与零件装配的顺序,一般用来创建较为复杂的对象
5、单例模式
定义:在任何给定时间只因运行一个类或某个类的一组预定义数量的实例
(1).饿汉模式
从名字是也可以很好理解,就是“比较勤快”,实例在初始化的时候就已经建好了,不管你有没有用到,都想创建好了再说
好处是没有线程安全的问题
坏处是浪费内存空间
代码示例:
package com.creation_Pattern.p06_singleton.t1;
/**
* @author: 皮先生kx
* @Description: 单例模式——饿汉模式(类装载的时候就是初始化对象)
* @date: created on 2021/10/17 20:44
* @email: 2472177078@qq.com
*/
public class Singleton {
//属性要求私有静态
private static Singleton instance = new Singleton();
//控制实例的数量,我们间构造器进行私有化,测试类Test中就不能使用new关键字创建对象了
private Singleton(){
}
//上述两个都是私有的,外部都无法访问,当类装载的时候就是初始化对象,用的时候就可以通过下面的方法获得唯一一个实例
public static Singleton getInstance(){
return instance;
}
}
代码示例:
package com.creation_Pattern.p06_singleton.t1;
/**
* @author: 皮先生kx
* @Description: 测试类
* @date: created on 2021/10/17 20:44
* @email: 2472177078@qq.com
*/
public class Test {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
Singleton s = Singleton.getInstance();
System.out.println(s);
}
}
}
运行结果:
"C:\Program Files\Java\jdk1.8.0\bin\java.exe"
com.creation_Pattern.p06_singleton.t1.Singleton@4554617c
com.creation_Pattern.p06_singleton.t1.Singleton@4554617c
com.creation_Pattern.p06_singleton.t1.Singleton@4554617c
com.creation_Pattern.p06_singleton.t1.Singleton@4554617c
com.creation_Pattern.p06_singleton.t1.Singleton@4554617c
Process finished with exit code 0
(2).懒汉模式
实例在用到的时候才去创建,“比较懒” ,用的时候才去检查有没有实例,没有则创建
有线程安全和不安全的两种写法,区别就是synchronized关键字
代码示例:
package com.creation_Pattern.p06_singleton.t2;
/**
* @author: 皮先生kx
* @Description: 单例模式——懒汉模式(实例在用的时候再创建)
* @date: created on 2021/10/17 20:44
* @email: 2472177078@qq.com
*/
public class Singleton {
//属性要求私有静态,没有初始化,此时的实例为null
private static Singleton instance;
//控制实例的数量,我们间构造器进行私有化,测试类Test中就不能使用new关键字创建对象了
private Singleton(){
}
//上述两个都是私有的,外部都无法访问
//当多线程中两个线程同时第一次访问,同时为空,同时初始化容易出现两个实例对象,在此可以加入synchronized关键字,只能有一个线程访问,不好的地方就是影响效率
public static synchronized Singleton getInstance(){
if (instance==null){
instance = new Singleton();
}
return instance;
}
}
(3).双检锁
双检锁,又叫双重校验锁,在懒汉模式的基础上,通过synchronized关键字在内外都加了一层if条件判断,这样既保证了线程安全,又比直接上锁提高了执行效率,还节省了内存空间
代码示例:
package com.creation_Pattern.p06_singleton.t3;
/**
* @author: 皮先生kx
* @Description: 单例模式——双检锁
* @date: created on 2021/10/17 20:44
* @email: 2472177078@qq.com
*/
public class Singleton {
//属性要求私有静态,没有初始化,此时的实例为null
private volatile static Singleton instance;
//控制实例的数量,我们间构造器进行私有化,测试类Test中就不能使用new关键字创建对象了
private Singleton(){
}
//解决效率低:方法加了synchronized,只能一个一个执行,并不能并发执行,效率低
public static Singleton getInstance(){
//当上面方法去掉synchronized,多个线程同时访问时,下面代码块不执行,直接return instance;
if (instance==null){
synchronized (Singleton.class){
if (instance==null){
instance = new Singleton();
}
}
}
return instance;
}
}
//可能造成指令重排,仍然存在不安全情况
//为了避免指令重排造成的不安全,需要加入volatiled,后面在CPU中执行中就不会指令重排
(4).静态内部类
静态内部类的方式效果类似双检锁,但是实现更加简单,当这种方式只适用于静态域的情况,双检锁方式可以在实例域需要延迟初始化时使用。
package com.creation_Pattern.p06_singleton.t4;
/**
* @author: 皮先生kx
* @Description: 单例模式——静态内部类
* @date: created on 2021/10/17 20:44
* @email: 2472177078@qq.com
*/
public class Singleton {
//延迟装载,懒汉模式
private Singleton(){
}
private static class SingletonHolder{
private final static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
(5).枚举
枚举的方式更加简洁清晰,并且她还自动支持序列化机制,绝对防止多次实例化
代码示例:
package com.creation_Pattern.p06_singleton.t5;
/**
* @author: 皮先生kx
* @Description: 单例模式——枚举
* @date: created on 2021/10/17 20:44
* @email: 2472177078@qq.com
*/
public enum Singleton {
//每一个枚举的类型都是一个静态的常量
INSTANCE;
public void doSomething(){
System.out.println("doSomething .");
}
}
代码示例:
package com.creation_Pattern.p06_singleton.t5;
/**
* @author: 皮先生kx
* @Description:
* @date: created on 2021/10/17 21:15
* @email: 2472177078@qq.com
*/
public class Test {
public static void main(String[] args) {
Singleton.INSTANCE.doSomething();
}
}
6、原型模式
定义:用一个已经创建好 的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象
拷贝是直接在堆中进行,这其实也可以理解,new的时候,JVM要走一趟类加载流程,这个流程非常麻烦,在类加载流程中会调用构造函数,最后生成的对象会放在堆中国,而拷贝就是直接拷贝堆中的现成的二进制对象,然后重新分配一个内存块。
原型模式的克隆分为:浅克隆和深克隆
浅克隆:创建一个新对象,新对象的属性和原理对象完全相同,对于非基本数据类型,仍指向原有属性所指的对象的内存地址
深克隆:创建一个新对象,属性中应用的其他对象也会被克隆,不再指向原有对象地址
代码示例:
package com.creation_Pattern.p07_prototype;
//以该Student类作为克隆的原型
public class Student implements Cloneable{
private String name;
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//重写父类方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
代码示例:
package com.creation_Pattern.p07_prototype;
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Student s1 = new Student();
s1.setName("张三");
s1.setAge(20);
Student s2 = (Student) s1.clone();
System.out.println(s1);
System.out.println(s1.getName()+","+s1.getAge());
System.out.println(s2);
System.out.println(s2.getName()+","+s2.getAge());
}
}
运行结果:
"C:\Program Files\Java\jdk1.8.0\bin\java.exe"
com.creation_Pattern.p07_prototype.Student@4554617c
张三,20
com.creation_Pattern.p07_prototype.Student@74a14482
张三,20
Process finished with exit code 0
//可以看到属性相同,但是地址是不一样的
原型模式的优点:
- Java自带的原型模式基于内存二进制流的复制,在性能上比直接new一个对象更加优良
- 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一个状态),可辅助实现撤销操作
原型模式的缺点:
- 需要为每一个类都配置一个clone方法
- clone方法位于类的内存,当对已有类进行改造的时候,需要修改代码,违背了开闭原则
- 当实现深克隆的时候,需要编写较为复杂的代码,而且当对象之间存在多重嵌套应用是,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来比较麻烦。因此深克隆、浅克隆需要运行得当。