java设计模式--创建型模式

首先还是明确一下设计模式的原则

SOLID原则

  1. 单一职责原则:软件模块应该只有一个被修改的理由

大部分都会应用于类

Class  Car  {
    private String name;
    private String model;
    private String year;
    public void setName(){}
    public void create(){}
    public void read(){}
    public void update(){}
    public void delete(){}
    public void calculate(){}
}

上面的类封装了car的逻辑和数据库操作,两个职责

要拆成两个类:

Class  Car  {
    private String name;
    private String model;
    private String year;
    public void calculate(){}
}
Class  CarDao{
    public void setName(){}
    public void create(){}
    public void read(){}
    public void update(){}
    public void delete(){}
}

2. 开闭原则:模块、类和函数应该对扩展开放,对修改关闭

开发完后不要动那个函数,解决方法:多态扩展、继承

3. 里氏替换原则(LSP):子类(派生类)必须完全可替代其基类

List<String> list = new ArrayList<String>()

4. 接口隔离原则:客户端不应该依赖于它所不需要的接口

5. 依赖倒置原则:高级模块不应该依赖低级模块,两者都应该依赖抽象;

                            抽象不应该依赖于细节,细节应该依赖于抽象

------------------------------------正文--------------------------------

创建型模式主要用于处理对象的创建问题,分为:

单例、工厂、对象池、原型、建造者模式

一、单例模式

保证一个对象只能创建一个实例,还要提供对实例的全局访问方法,基础例子:

public class Singleton {
    //首先有private的实例,static是为了getInstance方法能调用它
    private static Singleton instance;
    //把构造方法私有化,不让外部调用构造方法,保证只有一个实例
    private Singleton(){}
    //静态方法返回单一实例,可以在外部直接类名.getInstance()调用
    public static Singleton getInstance(){
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
    //这个类要做的其他事情
    public void doSomething(){}
}

调用:

Singleton.getInstance().doSomething();

1. 同步锁单例模式

问题:多线程中,如果实例为空,当两个线程同时调用getInstance(),会发生并发问题

解决方法:

(1)

public static synchronized Singleton getInstance(){}

(2)用synchronized代码块包装if (instance == null)条件,使用时,要提供一个对象来提供锁

public static Singleton getInstance(){
    synchronized (Singleton.class){
        if (instance == null) {
            instance = new Singleton();
        }
    }
    return instance;
}

2.拥有双重校验锁机制的同步锁单例模式

问题:只有未实例化的情况下,才需要走锁住的代码,而大部分情况都是不需要的。

解决方法:只有在单例模式未实例化的情况下,才能在synchronized 代码块前添加附加条件移动线程安全锁

第一个if判断是为了在singleton对象已经创建的情况下,避免进入同步代码块,提升效率。第二个if判断能拦截除第一个获得对象锁线程以外的线程,当线程A获取到锁并实例化完instance后,即便线程B已经过了第一个判断,在A释放锁后B才获取锁,然后又判断了instance有没有实例化,此时线程A已经实例化过了,B不会再new了

if (instance == null) {
    synchronized (Singleton.class) {
        if (instance == null) {
            instance = new Singleton();
        }
    }
}

3. 无锁的线程安全单例模式

直接在声明的时候就new

public class Singleton {
    //把构造方法私有化,不让外部调用构造方法,保证只有一个实例
    private Singleton() {}
    
    //首先有private的实例,static是为了getInstance方法能调用它
    private static final Singleton instance = new Singleton();
    
    //静态方法返回单一实例,可以在外部直接类名.getInstance()调用
    public static Singleton getInstance() {
        return instance;
    }

    //这个类要做的其他事情
    public void doSomething() {}
}

4.提前加载和延迟加载

按照实例对象被创建的时机,单例模式可以分为两类:应用开始时创建单例实例,称为提前加载单例实例;getInstance首次被调用时创建,则为延迟加载

无锁线程安全单例模式在早期版本的java中认为是提前加载的,最新java版本中,类只有在使用时才会被加载,所以也是一种延迟加载。

二、工厂模式

Vehicle vehicle =  new Car()

上述代码说明了Vehicle和Car之间的依赖关系,假如现在Car要替换为Truck,就要修改为

Vehicle vehicle =  new Truck()

不满足开闭原则(修改了写好的代码)和单一职责原则(主类除了固有功能外,还要实例化vehicle对象)

此时就需要工厂模式,工厂模式用于实现逻辑的封装,并通过公共的接口提供对象的实例化服务,在添加新类时只需要少量的修改

1. 简单工厂模式

封装一个simpleFactory类和createProduct()方法,实例化对象时,根据传入参数的不同,创建不同的具体产品并返回,返回的对象被转换为基类

(1)静态工厂模式

public class VehicleFactory {
    public enum VehicleType{
        Bike,Car,Truck;
    }
    public static Vehicle create(VehicleType type){
        switch (type){
            case Bike: return new Bike();
            case Car: return new Car();
            case Truck: return new Truck();
            default: return null;
        }
    }
}

abstract class Vehicle{}
class Bike extends Vehicle{}
class Car extends Vehicle{}
class Truck extends Vehicle{}

这样factory只负责实例化对象,符合单一职责謮,但是每增加一个vehicle的子类,要对factory进行修改,违反开闭原则,所以:修改使得注册的新类在使用时才会被实例化,有以下两种方式:

(2)使用反射机制注册产品类对象和实例化

public class VehicleFactory {
    private Map<String,Class> registeredProducts = new HashMap<>();
    //注册新类的方法
    public void registerVehicle(String vehicleId, Class vehicleClass){
        registeredProducts.put(vehicleId, vehicleClass);
    }
    public Vehicle create(String type) throws IllegalAccessException, InstantiationException {
        Class productClass = registeredProducts.get(type);
        return (Vehicle) productClass.newInstance();
    }
}

但是,反射机制需要运行时权限,不一定能实现,且性能不高

(3)使用newInstance方法进行类注册的简单工厂模式

首先,Vehicle基类中添加抽象方法:

abstract public Vehicle newInstance();

对于每种产品,去实现这个方法

@Override
public Car newInstance(){
    return new Car();
}

 工厂类中,修改:

private Map<String,Vehicle> registeredProducts = new HashMap<>();
public void registerVehicle(String vehicleId, Vehicle vehicle){
    registeredProducts.put(vehicleId, vehicle);
}
public AbstractProduct create(String vehicleId) {
    return registeredProducts.get(vehicleId).newInstance();
}

2. 工厂方法模式

工厂类被抽象化,用于实例化特定产品类的代码转移到实现抽象方法的子类中,这样不需要修改就可以扩展工厂类。

几个概念:
产品接口(基类)

abstract class Vehicle{}

具体产品

class sportCar extends Vehicle{}
class SedanCar extends Vehicle{}

抽象工厂类:

public abstract class VehicleFactory {
    protected abstract Vehicle createVehicle(String item);
    public Vehicle orderVehicle(String size, String color){
        Vehicle vehicle = createVehicle(size);
        vehicle.setColor(color);
        return vehicle;
    }
}

具体工厂类:实现实例化方法

class CarFactory extends VehicleFactory {

    @Override
    protected Vehicle createVehicle(String size) {
        if (size.equals("small")) {
            return new SportCar();
        } else if (size.equals("large")) {
            return new SedanCar();
        } else {
            return null;
        }
    }
}

使用,创建工厂类并创建订单:

VehicleFactory carFactory = new CarFactory();
carFactory.orderVehicle("large","blue")

后续:新建工厂类TruckFactory,不修改就可以扩展工厂方法

3. 抽象工厂模式

工厂方法模式只考虑生产同等级的产品,但是在现实生活中许多工厂是综合型的工厂,能生产多等级(种类) 的产品

举例:

image.png

    

抽象工厂模式不再是创建单一类型的对象,而是创建一系列相关联的对象。如果说工厂方法模式中只包含了一个抽象产品类,那么抽象工厂模式中包含多个抽象产品类

4.工厂模式总结:

总的来说

(1)简单工厂模式适合于类不多且不会新增的情况,静态工厂模式中的一堆switch case适合这种情况(新增一个类需要多加一个case),newInstance中的map也适合(新增一个类,需要去注册到map中)

(2)工厂方法模式适合于单一种类的产品,一次构建的对象只能是一种动物,一种汽车,一种植物,可以在一个产品下新增多种类。当一个工厂就可以完成任务时,退化为简单工厂模式。

(3)抽象工厂模式适合于一次需要多种类的产品,只需要构造一个可以创建多个产品的工厂,然后创建相应对象即可,但对于已经构造出来的工厂,不能构造出这个工厂里没有的类对象。当只有一种产品时,退化为工厂方法模式

三、建造者模式

当需要实例化一个复杂的类,以得到不同结构和不同类内部状态的对象时,我们可以使用不同的类对它们的实例化操作逻辑分别封装,这些类就是建造者(Builder)

实际上就是Builder().build()

角色:产品类、抽象建造者类builder、具体建造者类、导演类(Director)

汽车建造者样例

有一个Car类,不能每新增一种Car,就多写一个构造函数,所以适合用建造者模式

CarBuilder是建造者基类,包含四个抽象方法

两个具体建造者类,ElectricCarBuilder和GasolineCarBuilder,分别实现CarBuilder的所有抽象方法

导演类负责决定调用那个具体建造者类的哪些方法并传入参数即可

不是一个好的可应用的方式,因为还是太具体,对于一个具体类就要用一个具体建造者,不好用,还是要直接使用Builder().build()方便,只需要加上注解@Data即可

四、原型模式

实际上是一种克隆对象的方法,涉及:

抽象原型类:声明clone()方法的基类或接口,简单场景下,不需要这种基类。Object就有clone()方法,而所有的类都继承了Object,所以都有clone方法

具体原型类:实现或扩展clone()方法的类,调用具体类的clone()方法实现克隆一个对象。

五、对象池模式

重用和共享创建成本高昂的对象,包括封装外部资源的对象(创建时会耗费很多资源)

角色:

资源池类:用于封装逻辑的类,保存和管理资源列表

资源类:用于封装资源的类。资源类通常被资源池类引用,因此只要资源池不重新分配,它就不会被回收

客户端:使用资源的类

客户端需要新资源时,向资源池申请:

public Resource acquireResource(){
    if (avaliable.size() <= 0){
        Resource resource = new Resource();
        inuse.add(resource);
        return resource;
    }else {
        return avaliable.remove(0);
    }
}

客户端用完后需要释放资源,资源会重新回到资源池

public void releaseResource(Resource resource){
    avaiable.add(resource);
}

典型用例:数据库连接池

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值