工厂模式
工厂模式是为了更好管理new出来的对象, 把创建对象的任务交给工厂做, 比手动new更符合软件设计原则
简单工厂模式
包括三个角色
- 工厂角色
- 抽象产品角色
- 具体产品角色
工厂角色返回的值是抽象产品角色, 但是实际上的业务逻辑都是具体产品角色, 是在工厂角色内部实例化初始化的
简单工厂把对象创建和对象使用分开了
优点:
- 工厂类进行判断逻辑, 客户端不用创建产品对象, 实现创建使用分离
- 客户端也不用记住产品类名称, 只用记得对应参数即可
- 也可以引入配置文件, 做到对代码不侵入
缺点
- 工厂类职责过重, 如果工厂类不能工作, 系统全膨蝰
- 工厂类逻辑会随着产品增多而复杂
工厂方法模式
工厂方法模式是简单工厂的一种优化手段, 简单工程的工厂类过于复杂, 工厂方法模式不再提供统一的工厂类创建所有产品对象, 而是针对不同产品提供不同工厂。
包含4个部分
- 抽象产品
- 具体产品
- 抽象工厂
- 具体工厂
这里的抽象工厂等同于简单工厂中的工厂类,但是工厂方法模式提供具体工厂, 针对性的生产产品,将产品实例化操作延迟到子类具体工厂中,不必频繁修改抽象工厂类,解决简单工厂模式中违背开闭原则的问题
其实工厂方法模式也透露出一股多态的味道, 也叫多态工厂模式
优点:
- 用户只用关心产品对应工厂,无需关心创建细节
- 新增产品时候, 不用修改抽象工厂和抽象产品提供的接口,只需要增加一个具体工厂和具体产品租组,系统扩展性很高
缺点:
- 每新增一个产品,就要增加对应的具体产品和具体工厂,当产品多了,系统中的类也将成对增加,编译运行时间也带来开销
抽象工厂模式
简单工厂模式工厂类职责过重出现了工厂方法模式,但是工厂方法模式中,一个产品类只生产一种产品,这样导致存在很多工厂类,我们考虑,将共性产品组成一个产品组,由统一工厂来生产。这就是抽象工厂模式的核心思想
包含四个角色:
- 抽象工厂
- 具体工厂
- 抽象产品
- 具体产品
此处抽象工厂模式并不只是定义生产某一产品的方法,而是定义同属于一产品族的产品
对应的具体工厂也是生产这一产品族的产品
优点:
- 新增产品族无须改变已有系统,符合开闭原则
- 用户只用关心产品对应工厂,无需关心创建细节,并且能够保证客户端只使用同一产品族中的对象
缺点:
- 如果要在抽象工厂中新增某产品,需要对原有系统做较大修改,违背开闭原则,所以使用抽象工厂模式的时候,一定要对系统做好设计,否则后期修改需要耗费大量精力
单例模式
说起单例模式,不得不回想起学习synchronized的那段时光。。。
为什么出现单例,也是为了确定对象的唯一性,避免多个对象带来种种问题,例如,电脑的任务管理器,是只能打开一个的,如果说有两个任务管理器,那究竟以哪个中的数据为主?
单例模式也分为懒汉模式和饿汉模式。
懒汉模式
懒汉模式的意义在于,这个单例对象只有在不得以使用的时候才创建出来,“懒懒的”,因此叫做懒汉模式
class LazySimpleSingleton{
private static LazySimpleSingleton lazySimpleSingleton = null;
public static LazySimpleSingleton getInstance(){
if(lazySimpleSingleton == null) {
lazySimpleSingleton = new LazySimpleSingleton();
}
return lazySimpleSingleton;
}
}
懒汉模式一般还可以优化,结合synchroinzed成双重校验锁定,避免高并发时候出现线程安全问题
饿汉模式
饿汉模式的意义在于,这个单例对象不管我想不想用都早早创建出来了,“像没吃过饭一样着急”,因此叫饿汉模式
class Singleton{
private static final Singleton singleton = new Singleton();
private Singleton(){
}
public static Singleton getInstance() {
return singleton;
}
}
饿汉模式不能延迟加载,不使用就会白白占用内存,懒汉模式又存在线程安全问题,性能可能受影响,是否还有更好的方式创建单例对象呢?
静态内部类
/**
* 内部类是在使用的时候才会调用
*/
class LazyStaticInnerClassSingleton{
private LazyStaticInnerClassSingleton lazyStaticInnerClassSingleton(){}
private static LazyStaticInnerClassSingleton getInstance(){
return LazyHolder.INSTANCE;
}
private static class LazyHolder{
private static final LazyStaticInnerClassSingleton INSTANCE = new LazyStaticInnerClassSingleton();
}
}
内部类在使用时候才会调用,因此具备延迟加载;调用getInstance的时候,JVM加载这个内部类会保证线程安全性。
原型模式
原型模式也是克隆模式,核心在于克隆已有对象。
如果我们手写一个克隆方法,就要考虑手动将对象的各种属性进行拷贝,Java底层给我们提供了方法可以简化我们
的体力劳动,所有对象都有一个共同父类就是Object类,这个类中就有clone可以作为浅克隆,注意在使用clone方法的时候,该对象一定要继承Cloneable接口,否则调用clone的时候会报错CloneNotSupportedException。
单纯调用clone方法只是浅克隆,遇到包含引用类型对象的时候,克隆对象中的引用类型会和原对象的引用类型公用同一个空间。Java中要想实现深克隆,我们可以通过序列化的方式,将对象写入流,对这个流对象进行拷贝,然后再反序列化回来,即可实现深克隆。实现深克隆的条件就是该类必须实现Serializable接口。
优点:
- 创建对象实例复杂时,可以使用原型模式简化创建过程,复制一个已有的实例对象提高效率
- 使用深克隆拷贝对象,保持对象状态,在将来某一个时刻还能反悔到原来的状态
缺点:
- 每一个类都配备一个克隆方法,如果以后需要修改克隆逻辑,违反开闭原则
建造者模式
我在有了Spring的一些基础上理解建造者,它的意思是不是等同于依赖注入?在注入的时候考虑注入顺序以及注入的逻辑即可
建造者包含4个角色:
- 抽象建造者
- 具体建造者
- 产品角色
- 指导者
抽象建造者中抽象了众多创建产品角色的各个部分的方法,具体建造者对其进行具体实现,并可以使用getResult等方法返回创建的产品。
指导者主要是为了隔离客户和创建过程,并控制产品创建的过程(创建产品各部分的顺序等)
class Product {
private String partA; //定义部件,部件可以是任意类型,包括值类型和引用类型
private String partB;
private String partC;
//partA的Getter方法和Setter方法省略
//partB的Getter方法和Setter方法省略
//partC的Getter方法和Setter方法省略
}
abstract class Builder {
//创建产品对象
protected Product product=new Product();
public abstract void buildPartA();
public abstract void buildPartB();
public abstract void buildPartC();
//返回产品对象
public Product getResult() {
return product;
}
}
class Director {
private Builder builder;
public Director(Builder builder) {
this.builder=builder;
}
public void setBuilder(Builder builder) {
this.builder=builer;
}
//产品构建与组装方法
public Product construct() {
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
return builder.getResult();
}
}
对于客户端来说,只需要关系建造者就可以了,不需要关心产品对象的具体组装过程,只需要指定具体的建造者类型就行
Builder builder = new ConcreteBuilder(); //指定具体建造者类型
Director director = new Director(builder);
Product product = director.construct();
PS
感觉只有单例原型模式最熟悉了,尤其是后面的结构型模式看的头痛,不和源码结合一下很难理解。。。之后可能还得重新总结一版