java设计模式之创建者模式

一、单例设计模式

涉及到单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建,这个类提供了一种访问其唯一对象的方式,可以直接访问,不需要实例化该类的对象。
单例模式分为两种:

  • 饿汉式:类加载就会导致该实例对象被创建
  • 懒汉式:类加载不会导致该单例对象被创建,而是首次使用该对象时才会创建

(1)饿汉式:静态变量方式

public class Singleton {
    private Singleton(){};

    private static Singleton instance = new Singleton();

    public static Singleton getInstance(){
        return instance;
    }
}
public class test {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();

        Singleton instance2 = Singleton.getInstance();

        System.out.println(instance1 == instance2);
    }
}

image.png

(2)饿汉式:静态代码块

public class Singleton {
    private Singleton(){}

    private static Singleton instance;

    static {
        instance = new Singleton();
    }

    public static Singleton getInstance(){
        return instance;
    }
}
public class test {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1 == instance2);
    }
}

image.png

(3) 饿汉式:枚举方式

枚举类实现单例模式极力推荐的方式,因为枚举类型式线程安全的,并且只会装载一次,设计者充分利用了枚举的这个特性来实现单例模式,枚举的写法非常简单,而且枚举类型式所有单例实现中唯一一种不会被破坏的单例实现模式。

public enum Singleton {
    INSTANCE;
}
public class test {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.INSTANCE;
        Singleton instance2 = Singleton.INSTANCE;

        System.out.println(instance1 == instance2);
    }
}

(4)懒汉式: 加锁

public class Singleton {
    private Singleton(){};
    private static Singleton instance;

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
public class test {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1 == instance2);
    }
}

image.png

(5) 懒汉式:双重检查锁

解决了单例、性能、线程安全问题,但是双重检查锁模式上看上去完美无缺,其实存在问题,在多线程情况下,可能会出现空指针,出现的问题的原因式JVM在实例化对象的时候会进行优化和指令的重排序操作,要解决这个问题需要使用volatile关键字,volatile可以保证可见性和有序性。

public class Singleton {
    private Singleton(){};

    private static volatile Singleton instance;

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
public class test {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1 == instance2);
    }
}

image.png

(6)懒汉式:静态内部类方式

静态内部类单例模式中实例由内部类创建,由于JVM加载外部类的过程中,是不会加载静态内部类的,只有内部类的属性/方法被调用的时候才会被加载,并初始化其静态属性,静态属性被static修饰,保证只被实例化一次,并且严格保证实例化顺序。

public class Singleton {
    private Singleton(){};
    private static class SingletonHolder{
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}
public class test {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1 == instance2);
    }
}

image.png

破坏单例模式的话可以使用反射或序列化。

二、 原型模式

用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象

结构:
抽象原型类:规定了具体原型对象必须实现的clone方法
具体原型类:实现抽象原型类的clone方法,它是可被复制的对象
访问类:使用具体原型类中的clone方法来复制新的对象

原型模式的克隆分为深克隆和浅克隆:

  • 浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址
  • 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
    java中的object中提供了clone方法实现了浅克隆
public class RealizeType implements Cloneable{
    @Override
    public RealizeType clone() throws CloneNotSupportedException {
        System.out.println("具体的原型对象创建成功");
        return (RealizeType) super.clone();
    }
}
public class test {
    public static void main(String[] args) throws CloneNotSupportedException {
        RealizeType realizeType = new RealizeType();
        RealizeType clone = realizeType.clone();
        System.out.println(realizeType == clone); // false
    }
}

(1)浅克隆

import lombok.Data;

@Data
public class Citation implements Cloneable{
    private String name;

    @Override
    protected Citation clone() throws CloneNotSupportedException {
        return (Citation) super.clone();
    }

    public void show(){
        System.out.println(name + "同学,特发奖状!");
    }
}
public class test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Citation citation = new Citation();
        Citation clone = citation.clone();
        citation.setName("张三");
        clone.setName("李四");

        citation.show();
        clone.show();
    }
}

image.png

(2)深克隆

import lombok.Data;

import java.io.Serializable;

@Data
public class Citation implements Cloneable, Serializable {
    private Student stu;

    @Override
    protected Citation clone() throws CloneNotSupportedException {
        return (Citation) super.clone();
    }

    public void show(){
        System.out.println(stu.getName() + "同学,特发奖状!");
    }
}
import lombok.Data;

import java.io.Serializable;

@Data
public class Student implements Serializable {
    private String name;
}
import java.io.*;

public class test {
    public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
        Citation citation = new Citation();
        Student student = new Student();
        student.setName("张三");
        citation.setStu(student);

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("1.txt"));
        oos.writeObject(citation);
        oos.close();

        ObjectInputStream osi = new ObjectInputStream(new FileInputStream("1.txt"));
        Citation o = (Citation) osi.readObject();
        osi.close();

        o.getStu().setName("李四");

        citation.show();
        o.show();

    }
}

三、工厂模式

(1)简单工厂模式

简单工厂模式不是一种设计模式,比较像一种编程习惯

简单工厂模式包含如下角色:

  • 抽象产品:定义了产品的规范,描述了产品的主要特性和功能
  • 具体产品:实现或者继承抽象产品的子类
  • 具体工厂:提供了创建产品的方法,调用者通过该方法来创建产品

优缺点:
优点:

  • 封装了创建对象的过程,可以通过参数直接获取对象,把对象的创建和业务逻辑层分开,这样以后就避免了修改客户代码
  • 如果要实现新的产品直接修改工厂类,而不需要在原代码中修改,这样就降低了客户代码修改的可能性,更加容易扩展。
    缺点:
  • 增加新的产品时还是要修改工厂类的代码,违背了开闭原则。
public abstract class Coffee {
    public abstract String getName();
    public void addSugar(){
        System.out.println("add sugar");
    }
    public void addMilk(){
        System.out.println("add milk");
    }
}
public class AmericanCoffee extends Coffee{
    @Override
    public String getName() {
        return "美式咖啡";
    }
}
public class LatterCoffee extends Coffee{
    @Override
    public String getName() {
        return "拿铁咖啡";
    }
}
public class SimpleCoffeeFactory {
    public Coffee createCoffee(String type){
       Coffee coffee = null;
       if ("american".equals(type)) {
           coffee = new AmericanCoffee();
       } else if ("latter".equals(type)) {
           coffee = new LatterCoffee();
       }else {
           throw new RuntimeException("对不起,你点的咖啡没有");
       }
       return coffee;
    }
}
public class CoffeeStore {
    public Coffee orderCoffee(String type){
        SimpleCoffeeFactory simpleCoffeeFactory = new SimpleCoffeeFactory();
        Coffee coffee = simpleCoffeeFactory.createCoffee(type);
        coffee.addMilk();
        coffee.addSugar();
        return coffee;
    }
}
public class test {
    public static void main(String[] args) {
        CoffeeStore coffeeStore = new CoffeeStore();
        Coffee coffee = coffeeStore.orderCoffee("latter");
        System.out.println(coffee.getName());
    }
}

image.png

(2)扩展: 静态工厂

将工厂类中创建对象的功能定义为静态的,这个工厂就是静态工厂模式,他也不是23种设计模式中的。

public abstract class Coffee {
    public abstract String getName();
    public void addSugar(){
        System.out.println("add sugar");
    }
    public void addMilk(){
        System.out.println("add milk");
    }
}
public class LatterCoffee extends Coffee {
    @Override
    public String getName() {
        return "拿铁咖啡";
    }
}
public class AmericanCoffee extends Coffee {
    @Override
    public String getName() {
        return "美式咖啡";
    }
}
public class SimpleCoffeeFactory {
    public static Coffee createCoffee(String type){
       Coffee coffee = null;
       if ("american".equals(type)) {
           coffee = new AmericanCoffee();
       } else if ("latter".equals(type)) {
           coffee = new LatterCoffee();
       }else {
           throw new RuntimeException("对不起,你点的咖啡没有");
       }
       return coffee;
    }
}
public class CoffeeStore {
    public Coffee orderCoffee(String type){
        Coffee coffee = SimpleCoffeeFactory.createCoffee(type);
        coffee.addMilk();
        coffee.addSugar();
        return coffee;
    }
}
public class test {
    public static void main(String[] args) {
        CoffeeStore coffeeStore = new CoffeeStore();
        Coffee coffee = coffeeStore.orderCoffee("latter");
        System.out.println(coffee.getName());
    }
}

(3)工厂方法模式

针对简单工厂模式的缺点,使用工厂方法就可以解决,因为工厂方法模式完全遵循开闭原则。

概念:
定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象,工厂方法使一个产品类的实例化延迟到其工厂的子类

结构:

  • 抽象工厂:提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品
  • 具体工厂:主要是实现抽象工厂中的抽象方法完成具体工厂的创建
  • 抽象产品:定了产品的规范,描述了产品的主要特性与功能
  • 具体产品:实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。

优点:

  • 用户只需要知道具体工厂的名称就可以得到所要的产品,无须知道产品的具体创建过程
  • 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则
    缺点:
  • 每增加一个产品就要新增一个具体产品类和对应的具体工厂类,这增加了系统复杂度
public abstract class Coffee {
    public abstract String getName();
    public void addSugar(){
        System.out.println("add sugar");
    }
    public void addMilk(){
        System.out.println("add milk");
    }
}
public class AmericanCoffee extends Coffee {
    @Override
    public String getName() {
        return "美式咖啡";
    }
}
public class LatterCoffee extends Coffee {
    @Override
    public String getName() {
        return "拿铁咖啡";
    }
}
public interface CoffeeFactory {
    Coffee createCoffee();
}
public class AmericanCoffeeFactory implements CoffeeFactory{
    @Override
    public Coffee createCoffee() {
        return new AmericanCoffee();
    }
}
public class LatterCoffeeFactory implements CoffeeFactory{
    @Override
    public Coffee createCoffee() {
        return new LatterCoffee();
    }
}
public class CoffeeStore {
    private CoffeeFactory factory;

    public void setFactory(CoffeeFactory factory){
        this.factory = factory;
    }
    public Coffee orderCoffee(){
        Coffee coffee = factory.createCoffee();
        coffee.addMilk();
        coffee.addSugar();
        return coffee;
    }
}
public class test {
    public static void main(String[] args) {
        CoffeeStore coffeeStore = new CoffeeStore();
        CoffeeFactory americanCoffeeFactory = new AmericanCoffeeFactory();
        coffeeStore.setFactory(americanCoffeeFactory);

        Coffee coffee = coffeeStore.orderCoffee();
        System.out.println(coffee.getName());
    }
}

(4)抽象工厂模式

抽象工厂模式将考虑多等级产品的生产,将同一个具体的工厂所生产的位于不同等级的一组产品称为一个产品族。

概念:

  • 是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品
    的模式结构。
    抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可以生产多个等级的产品。

结构:

  • 抽象工厂:提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品。
  • 具体工厂:主要实现了抽象工厂中的多个抽象方法,完成具体的产品创建
  • 抽象产品:定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品
  • 具体产品:实现了抽象产品角色所定义 的接口,由具体工厂来创建,它同具体工厂之前是多对一的关系。

优点:

  • 当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象

缺点:

  • 当产品族中需要增加一个新的产品时,所有的工厂类都需要修改

使用场景:

  • 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机,洗衣机,空调等。
  • 系统中有多个产品族,但每次只使用其中某一族产品。如有人只喜欢穿某一品牌的衣服和鞋
  • 系统中提供了产品的类库,且所有的产品的接口相同,客户端不依赖产品实例的创建细节和内部结构
    如,输入法换皮肤,一整套一起换
public abstract class Coffee {
    public abstract String getName();
    public void addSugar(){
        System.out.println("add sugar");
    }
    public void addMilk(){
        System.out.println("add milk");
    }
}
public class AmericanCoffee extends Coffee {
    @Override
    public String getName() {
        return "美式咖啡";
    }
}
public class LatterCoffee extends Coffee {
    @Override
    public String getName() {
        return "拿铁咖啡";
    }
}
public abstract class Dessert {
    public abstract void show();
}
public class MatchaMousse extends Dessert{
    @Override
    public void show() {
        System.out.println("抹茶慕斯");
    }
}
public class Tiramisu extends Dessert{
    @Override
    public void show() {
        System.out.println("提拉米苏");
    }
}
public interface DessertFactory {
    Coffee createCoffee();
    Dessert createDessert();
}
public class AmericanDessertFactory implements DessertFactory{
    @Override
    public Coffee createCoffee() {
        return new AmericanCoffee();
    }

    @Override
    public Dessert createDessert() {
        return new MatchaMousse();
    }
}
public class LatterDessertFactory implements DessertFactory{
    @Override
    public Coffee createCoffee() {
        return new LatterCoffee();
    }

    @Override
    public Dessert createDessert() {
        return new Tiramisu();
    }
}
public class test {
    public static void main(String[] args) {
        LatterDessertFactory latterDessertFactory = new LatterDessertFactory();
        Coffee coffee = latterDessertFactory.createCoffee();
        Dessert dessert = latterDessertFactory.createDessert();
        System.out.println(coffee.getName());
        dessert.show();
    }
}

模式扩展(简单工厂+配置文件解除耦合):

  • 可以通过工厂模式+配置文件的方式解除工厂对象和产品对象的耦合,在工厂类中加载配置文件中的全类名,并创建对象进行存储
    客户端如果需要对象,直接进行获取即可。

四、建造者模式

概述:

  • 将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示,分离了部件的构造(由builder来负责)和装配(Director负责),从而可以构造出复杂的对象,这个模式适用于:某个对象的构建过程复杂的的情况。
  • 由于实现了构建和装配的解耦。不同的构建器,相同的装配,也可以做出不同的对象,相同的构建器,不同的装配顺序可以做出不同的对象,也就实现了构建算法、装配算法的解耦,实现了更好的复用
  • 建造者模式可以将部件和其组装过程分开,一步步创建一个复杂的对象,用户只需要指定复杂对象的类型就可以得到该对象,而无须知道其内部复杂的构造细节

结构:

  • 抽象建造者类:这个接口规定要实现复杂对象的那些部分的创建,并不涉及具体部件对象的创建
  • 具体建造者类:实现builder接口,完成复杂产品的各个部件的具体创建方法,在构造完成之后,提供产品的实例
  • 产品类:要创建的复杂对象
  • 指挥者类:调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品信息,只负责保证对象的各个部分完整的创建或者按照某个顺序创建

优点:

  • 建造者模式封装性好,使用建造者模式可以有效的封装变化,在使用建造者模式的场景中,一般产品类和建造者类是比较稳定的,因此将主要的业务逻辑封装在指挥者类中对整体而言可以取得比较好的稳定性。
  • 在建造者模式中,客户端不必知道产品的内部组成的细节,将产品本身与产品创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
  • 可以更加精细的控制产品的创建过程,将复杂的产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
  • 建造者模式很容易进行扩展,如果有新的需求,通过实现一个新的建造者类就可以完成,基本上不用修改之前已经通过测试的代码。因此也就不会对原有的功能引入风险,符合开闭原则。

缺点:

  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间差异性很大,则不适合使用建造者模式,因此其使用范围受到一定限制。
@Data
public class Bike {
    private String frame;
    private String seat;
}
public abstract class Builder {
    protected Bike bike = new Bike();
    public abstract void buildFrame();
    public abstract void buildSeat();

    public abstract Bike createBike();
}
public class MobileBuilder extends Builder{
    @Override
    public void buildFrame() {
        bike.setFrame("碳纤维车架");
    }

    @Override
    public void buildSeat() {
        bike.setSeat("真皮车座");
    }

    @Override
    public Bike createBike() {
        return bike;
    }
}
public class OfoBuilder extends Builder{
    @Override
    public void buildFrame() {
        bike.setFrame("铝合金车架");
    }

    @Override
    public void buildSeat() {
        bike.setSeat("橡胶车座");
    }

    @Override
    public Bike createBike() {
        return bike;
    }
}
public class Director {
    private Builder builder;

    public Director(Builder builder){
        this.builder = builder;
    }

    public Bike construct(){
        builder.buildFrame();
        builder.buildSeat();
        return builder.createBike();
    }
}
public class test {
    public static void main(String[] args) {

        Director director = new Director(new MobileBuilder());
        Bike bike = director.construct();
        System.out.println(bike.getFrame());
        System.out.println(bike.getSeat());
    }
}

五、总结

(1)工厂方法模式vs建造者模式

  • 工厂方法模式注重的是整体对象的创建方式,而建造者模式注重的是部件的构造过程,意在通过一步步地精确构造创建出一个相对复杂的对象。

(2)抽象工厂模式vs建造者模式

  • 抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品,具有不同分类纬度的产品组合,采用抽象工厂模式则不需要关心构建过程,只关心产品由什么工厂生产即可。
  • 建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装配件而产生一个产品
  • 如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值