面试没说清楚的design pattern(创建型模式)

学习目标:

常用的几种design pattern
利弊分别是什么?
具体举个例子?


学习内容:

创建型模式:
1、 单例模式singleton
2、 工厂模式factory
3、 建造者模式builder
4、 原型模式prototype

学习时间:

1、 周一至周五晚上 7 点—晚上9点

学习产出:

1、 视频10+个 2、技术笔记 3 篇

1.singleton单例模式

单例模式-23种设计模式系列

  1. 饿汉式实例化
    Eager Initialization,使用static定义这个instance,类一加载就生成这个instance。
package Singleton;
public class SingletonA {
   private static final SingletonA instance=new SingletonA();// the only copy
   //1.private constructor
   private SingletonA () { 
       if(instance!=null) {
           throw new RuntimeException("cannot created");
       }
   }
   //2.method: how to get instance:   class.getInstance()
   public static SingletonA getInstance(){
       //3.return the only copy of the class
       return instance;
   }
}

测试类:

package Singleton;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class SingletonTest {
   public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException {
       SingletonA a=SingletonA.getInstance();
       //cast to reflection:
       Constructor<SingletonA> c= (Constructor<SingletonA>) SingletonA.class.getDeclaredConstructors()[0];
       c.setAccessible(true);//无视私有构造器,
       SingletonA b=c.newInstance(); //通过反射new第二个instance
       System.out.println(a==b);//验证是否为同一个 //return false
   }
}

测试时,故意利用反射破坏单例模式【进攻】。
c.setAccessible(true);//无视私有构造器,
SingletonA b=c.newInstance(); //通过反射new第二个instance
System.out.println(a==b);//验证是否为同一个,结果预测会return false。
此时【防守】,
if(instance!=null) {
throw new RuntimeException(“cannot created”);
}发挥作用,抛出异常,防止单例模式被破坏。

Eager Initialization缺点:If never used, waste resources.

  1. 懒汉式实例化
package Singleton;
public class SingletonB {

   private static SingletonB instance=null;//the only copy
   //1.private constructor
   private SingletonB () {
   }
   //2.method: how to get instance:   class.getInstance()
   public static SingletonB getInstance(){
       if(instance==null) {
           instance = new SingletonB (); //2 threads will create 2 objs
       }
       //3.return the only copy of the class
       return instance;
   }
}

Lazy Initialization优点:solve the probelm of wasting resources.
Lazy Initialization缺点:not thread safe. 2 threads will create 2 objects.

  1. 加锁
package Singleton;
public class SingletonC {
   private static SingletonC instance=null;// the only copy
   //1.private constructor
   private SingletonC () {
   }
   //2.method: how to get instance:   class.getInstance()
   public synchronized static SingletonC getInstance(){ //syn, safe,but wait others.
       if(instance==null) {
           instance = new SingletonC ();
       }
       //3.return the only copy of the class
       return instance;
   }
}

加锁后线程安全了,但出现了互相等的情况,这个有必要吗?如果只是在new的阶段,两个threads就苦等对方,造成代码性能下降。

  1. volatile关键字+Double Check Lock
    best solution
package Singleton;
public class SingletonD {
   private volatile static SingletonD instance=null;// the only copy
   //1.private constructor
   private SingletonD () {  
   }
   //2.method: how to get instance:   class.getInstance()
   public static SingletonD getInstance(){ //2 threads
       if(instance==null) { //null
           synchronized (SingletonD.class) {//2 threads race for lock 这里锁class/方法都可以
               if (instance == null) {//1 thread get the lock   //then 2nd thread get not null.
                   instance = new SingletonD(); //1st create
               }
           }//1 st exist
       }
       //3.return the only copy of the class
       return instance;
   }
}

instance = new SingletonD()不是原子性操作,实际上有三步操作:
1。分配内存空间
2。执行构造函数,初始化对象
3。把对象reference指向这个空间
会出现指令重排,先132,或者先321
如果出现线程B,看到reference指向空间了,
以为instance!=null直接return instance;
实际上,instance还没有完成构造

solution:添加一个volatile关键字,从main memory快速读取,避免编译器指令重排(Compiler Instruction Reordering)

  1. enum枚举
    17:30利用enum枚举本身的特性,防止反射破坏单例
public enum EnumSingle {
    INSTANCE;

    public EnumSingle getInstance(){
        return INSTANCE;
    }
}

class Test{
    public static void main(String[] args) {
        EnumSingle instance1=EnumSingle.INSTANCE;
        EnumSingle instance2=EnumSingle.INSTANCE;
        System.out.println(instance1);
        System.out.println(instance2);
    }
}

枚举中通过jad反编译发现,实际上不存在无参构造,无法被反射破坏。
注解:jad反编译工具:Jad uses JAVA class name as an output file name。

2.factory工厂模式

【狂神说Java】通俗易懂的23种设计模式
核心思想:创建者和调用者分离
解决问题:生产具体产品。

  1. 简单工厂/静态工厂
public interface Car {
    void name();
}
public class Tesla implements Car {
    @Override
    public void name() {
		System.out.println("Tesla!");
    }
}
public class Wuling implements Car {
    @Override
    public void name() {
		System.out.println("Wuling!");
    }
}
public class CarFactory {
    public static Car getCar(String car) {
        if(car.equals("Wuling")){
            return new Wuling();
        }else if(car.equals("Tesla")){
            return new Tesla();
        }else{
            return null;
        }
    }
}
public class Consumer {
    public static void main(String[] args) {
        //不用new,只需要从工厂买
        Car car1=CarFactory.getCar("Wuling");
        Car car2=CarFactory.getCar("Tesla");
        car1.name();
        car2.name();
    }
}

优点:简单。消费者买车无需关心各种车型的实现细节。
缺点:不符合开闭原则(open to extension, close to modification)
如果需要增加大众汽车,就必须修改工厂模式中的代码。

  1. 工厂方法
    再为每种车型造一个工厂:
public interface Car {
    void name();
}
public interface CarFactory {
    Car getCar();
}
public class Tesla implements Car {
    @Override
    public void name() {
        System.out.println("Tesla!");
    }
}
public class TeslaFactory implements CarFactory {
    @Override
    public Car getCar() {
        return new Tesla();
    }
}
public class Wuling implements Car {
    @Override
    public void name() {
        System.out.println("Wuling!");
    }
}
public class WulingFactory implements CarFactory{
    @Override
    public Car getCar() {
        return new Wuling();
    }
}
public class Consumer {
    public static void main(String[] args) {
        Car car1= new TeslaFactory().getCar();
        Car car2=new WulingFactory().getCar();
        car1.name();
        car2.name();
    }
}

工厂方法是为Tesla、Wuling每个产品再造各自的工厂。
优点:若新增加大众汽车,可以拓展Dazhong类,DazhongFactory类,无需修改工厂模式中的代码。
缺点:每增加一种汽车,增加两个类,类多。
符合设计原则:工厂方法
实际应用:简单工厂

2.abstract factory抽象工厂模式

解决的问题:生产产品族。在这里插入图片描述

from:【狂神说Java】通俗易懂的23种设计模式
手机产品接口:

package com.kuang.designPattern.abstractFactory;

public interface PhoneProduct {
    void start();
    void shutdown();
    void call();
    void sendSMS();
}

路由产品接口

public interface RouterProduct {
    void start();
    void shutdown();
    void openwifi();
    void setting();
}

工厂接口

public interface ProductFactory {
    PhoneProduct producePhoneProduct();
    RouterProduct produceRouterProduct();
}

小米工厂:

public class XiaomiFactory implements ProductFactory{
    @Override
    public PhoneProduct producePhoneProduct() {
        return new XiaomiPhone();
    }

    @Override
    public RouterProduct produceRouterProduct() {
        return new XiaomiRouter();
    }
}
public class XiaomiPhone implements PhoneProduct {
    @Override
    public void start() {
        System.out.println("start Xiaomi Phone");
    }

    @Override
    public void shutdown() {
        System.out.println("shutdown Xiaomi Phone");
    }

    @Override
    public void call() {
        System.out.println("call from Xiaomi Phone");
    }

    @Override
    public void sendSMS() {
        System.out.println("send SMS from Xiaomi Phone");
    }
}
public class XiaomiRouter implements RouterProduct{
    @Override
    public void start() {
        System.out.println("start Xiaomi Router");
    }

    @Override
    public void shutdown() {
        System.out.println("shutdown Xiaomi Router");
    }

    @Override
    public void openwifi() {
        System.out.println("openwifi for Xiaomi Router");
    }

    @Override
    public void setting() {
        System.out.println("set for Xiaomi Router");
    }
}

华为工厂:

public class HuaweiFactory implements ProductFactory{
    @Override
    public PhoneProduct producePhoneProduct() {
        return new HuaweiPhone();
    }

    @Override
    public RouterProduct produceRouterProduct() {
        return new HuaweiRouter();
    }
}
public class HuaweiPhone implements PhoneProduct{
    @Override
    public void start() {
        System.out.println("start Huawei Phone");
    }

    @Override
    public void shutdown() {
        System.out.println("shutdown Huawei Phone");
    }

    @Override
    public void call() {
        System.out.println("call from Huawei Phone");
    }

    @Override
    public void sendSMS() {
        System.out.println("sendSMS from Huawei Phone");
    }
}
public class HuaweiRouter implements RouterProduct{
    @Override
    public void start() {
        System.out.println("start Huawei Router");
    }

    @Override
    public void shutdown() {
        System.out.println("shutdown Huawei Router");
    }

    @Override
    public void openwifi() {
        System.out.println("openwifi for Huawei Router");
    }

    @Override
    public void setting() {
        System.out.println("setting for Huawei Router");
    }
}
public class Client {
    public static void main(String[] args) {
        System.out.println("=====Xiaomi Product====");
        //xiaomi factory
        XiaomiFactory xiaomiFactory = new XiaomiFactory();
        PhoneProduct xPhone = xiaomiFactory.producePhoneProduct();
        xPhone.start();
        xPhone.call();

        RouterProduct xRouter = xiaomiFactory.produceRouterProduct();
        xRouter.start();
        xRouter.openwifi();
        System.out.println("=====Huawei Product====");
        //Huawei factory
        HuaweiFactory huaweiFactory=new HuaweiFactory();
        PhoneProduct hPhone = huaweiFactory.producePhoneProduct();
        hPhone.start();
        hPhone.call();
        RouterProduct hRouter=huaweiFactory.produceRouterProduct();
        hRouter.start();
        hRouter.openwifi();
    }
}

抽象工厂是“工厂的工厂”。由工厂接口拓展出小米工厂、华为工厂。
代码中,产品族分为小米族、华为族,产品分为手机、路由。
优点:无需关心代码实现细节,将一个系列的产品统一到一起创建
缺点:拓展新的产品难,增加了系统抽象性和理解难度。

混合使用太香了:策略设计模式+工厂模式+模板方法模式
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

3.builder建造者模式

抽象建造者

package com.kuang.builder;

public abstract class Builder {
    abstract void buildDiji();
    abstract void buildGangjing();
    abstract void buildDianxian();
    abstract void buildFenshua();

    abstract Product getProduct();
}

指挥者

public class Director {
    //核心顺序,由director定
    public Product build(Builder builder){
        builder.buildDiji();
        builder.buildDianxian();
        builder.buildGangjing();
        builder.buildFenshua();
        return builder.getProduct();
    }
}

房子

public class Product {
    private String diji;
    private String gangjing;
    private String dianxian;
    private String fenshua;

    public String getDiji() {
        return diji;
    }

    public void setDiji(String diji) {
        this.diji = diji;
    }

    public String getGangjing() {
        return gangjing;
    }

    public void setGangjing(String gangjing) {
        this.gangjing = gangjing;
    }

    public String getDianxian() {
        return dianxian;
    }

    public void setDianxian(String dianxian) {
        this.dianxian = dianxian;
    }

    public String getFenshua() {
        return fenshua;
    }

    public void setFenshua(String fenshua) {
        this.fenshua = fenshua;
    }

    @Override
    public String toString() {
        return "Product{" +
                "diji='" + diji + '\'' +
                ", gangjing='" + gangjing + '\'' +
                ", dianxian='" + dianxian + '\'' +
                ", fenshua='" + fenshua + '\'' +
                '}';
    }
}

具体执行者

public class Worker extends Builder{
    private Product product;

    public Worker() {
        product = new Product();//工人自己建产品
    }

    @Override
    void buildDiji() {
        product.setDiji("地基");
        System.out.println("地基");
    }

    @Override
    void buildGangjing() {
        product.setGangjing("钢筋");
        System.out.println("钢筋");
    }

    @Override
    void buildDianxian() {
        product.setDianxian("电线");
        System.out.println("电线");
    }

    @Override
    void buildFenshua() {
        product.setFenshua("粉刷");
        System.out.println("粉刷");
    }

    @Override
    Product getProduct() {
        return  product;
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        Director director=new Director();
        //director指挥worker,建造产品,
        Product build = director.build(new Worker());
        System.out.println(build.toString());
    }
}

Builder是一个抽象建造者,Worker extends Builder改写了各个方法。
Product类中主要写好getter和setter。最重要的工作的顺序,先打地基还是先铺电线是由director定的。

4.prototype原型模式

原型这个类,实现了Cloneable接口,new一个instance之后,副本就可以通过clone方法复制。

public class Video implements Cloneable{

    String name;
    Date date;

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

    public Video() {
    }

    public Video(String name, Date date) {
        this.name = name;
        this.date = date;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    @Override
    public String toString() {
        return "Video{" +
                "name='" + name + '\'' +
                ", date=" + date +
                '}';
    }
}
public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Date date=new Date();
        Video v1=new Video("kuangshenshuo",date);
        Video v2=(Video)v1.clone();//副本
        System.out.println("v1=>"+v1);
        System.out.println("v2=>"+v2);
        System.out.println("=======");
        date.setTime(22222);//修改日期
        System.out.println("v1=>"+v1);
        System.out.println("v2=>"+v2);//副本日期也跟着修改

    }
}

问题:修改原型的日期,那么副本的日期也跟着修改,这是浅拷贝。
要实现深拷贝,需要修改clone方法,将这个对象的属性也进行克隆。

 	@Override
    protected Object clone() throws CloneNotSupportedException {
        Object obj=super.clone();
        Video v=(Video) obj;
        v.date=(Date)this.date.clone();//将这个对象的属性也进行克隆
        return obj;
    }

这样即便原型v1的日期变化了,v2作为副本,有自己独立的日期,不会跟着变化。在这里插入图片描述

Thank you for reading !
Alt

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值