1、单例模式
写法:
懒汉式
/**
* 懒汉模式
* 声明一个静态对象,并且在第一次调用getInstance 方法时进行初始化
* Created by Bill on 2017/12/15.
*/
public class LazySingleInstance {
private static LazySingleInstance lazySingleInstance;
private LazySingleInstance() {
}
// 保证多线程下的安全
public static synchronized LazySingleInstance getLazySingleInstance() {
if (lazySingleInstance == null) {
lazySingleInstance = new LazySingleInstance();
}
return lazySingleInstance;
}
}
饿汉式:
/**
* CEo
* 饿汉单例模式
* 声明对象时就已经初始化了
*/
public class CEO extends Staff {
public static final CEO mCeo = new CEO();
// 构造函数私有化
private CEO() {
}
// 公有的静态方法 对外暴露获取单利的接口
public static CEO getCeo() {
return mCeo;
}
@Override
public void work() {
super.work();
// 管理vp
}
}
Double Check
/**
* double check lock
* 优点:资源利用高,第一次执行getInstance()方法才会实例化对象,效率高。
* 缺点:第一次加载反应稍慢,也由于Java内存模型的原因偶尔会失败。
* Created by Bill on 2017/12/16.
*/
public class Singleton {
private static Singleton singleton = null;
private Singleton() {
}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) { // 避免不必要的同步
if (singleton == null) { // 在null的情况下创建实例
singleton = new Singleton();
}
}
}
return singleton;
}
}
静态内部类
/**
* 静态内部类
* Created by Bill on 2017/12/16.
*/
public class InnerClassSingleton {
private InnerClassSingleton() {
}
public static InnerClassSingleton getInstance() {
return SingletonHolder.innerInstance;
}
/**
* 静态内部类
* 保证线程安全,也能保证单例对象的唯一性,同事也延迟加载了单利的实例化
*/
private static class SingletonHolder {
private static final InnerClassSingleton innerInstance = new InnerClassSingleton();
}
}
枚举
使用场景:
* ①系统中只需要存在一个对象的实例的时候
* ②系统中创建该对象的实例需要耗费很多资源,只许创建一个。
*
种类:
* 恶汉式:
没有同步问题,但是浪费资源;
* 懒汉式:
延迟加载但有线程安全问题;
通过关键字volatile和双重检查机制保证线程安全,volatile会保证多线程能够读取到变量的最新内存的值,
当一个线程修改变量的时候,立即会强迫修改内存中的变量的值。
但是volatile会屏蔽jvm的一些优化,影响效率。
* ioDH(Initialization Demand Holder):
解决延迟加载和线程安全的问题,在单例类中创建一个静态内部类,将对象的唯一实例放在内部类中
*
优缺点:
* 优点:
* 节省内存资源
* 可扩展为多例,可以解决多线程中单例对于性能的损耗
* 缺点:
* 单例类的职责过重,既是工厂,又包含业务方法或者产品角色。
* 在java语言中,对象被回收之后,单例对象的共享状态消失。
* 单例类没有抽象层,扩展单例类很困难
2、观察者模式
模式定义:
定义对象之间一对多的依赖关系,使得每当一个对象改变状态,则所有依赖与它的对象都会得到通知并被自动更新。
使用场景:
关联行为场景,需要注意的是:关联行为是可拆分的,而不是组合关系;
时间多级触发场景;
跨系统的消息交换场景,如:消息队列、事件总线处理机制。
角色构成:
● Subject(目标):目标又称为主题,
它是指被观察的对象。
在目标中定义了一个观察者集合,一个观察目标可以接受任意数量的观察者来观察,
它提供一系列方法来增加和删除观察者对象,
同时它定义了通知方法notify( )。目标类可以是接口,也可以是抽象类或具体类。
● ConcreteSubject(具体目标):具体目标是目标类的子类,通常它包含有经常发生改变的数据,
当它的状态发生改变时,
向它的各个观察者发出通知;同时它还实现了在目标类中定义的抽象业务逻辑方法(如果有的话)。
如果无须扩展目标类,则具体目标类可以省略。
● Observer(观察者):观察者将对观察目标的改变做出反应,观察者一般定义为接口,
该接口声明了更新数据的方法update(),
因此又称为抽象观察者。
● ConcreteObserver(具体观察者):在具体观察者中维护一个指向具体目标对象的引用,
它存储具体观察者的有关状态,
这些状态需要和具体目标的状态保持一致;它实现了在抽象观察者Observer中定义的update( )方法。
通常在实现时,可以调用具体目标类的attach( )方法将自己添加到目标类的集合中
或通过detach()方法将自己从目标类的集合中删除。
优缺点:
优点:
(1) 观察者模式可以实现表示层和数据逻辑层的分离,定义了稳定的消息更新传递机制,
并抽象了更新接口,使得可以 有各种各样不同的表示层充当具体观察者角色。
(2) 观察者模式在观察目标和观察者之间建立一个抽象的耦合。
观察目标只需要维持一个抽象观察者的集合,无须了解其具体观察者。
由于观察目标和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。
(3) 观察者模式支持广播通信,观察目标会向所有已注册的观察者对象发送通知,
简化了一对多系统设计的难度。
(4) 观察者模式满足“开闭原则”的要求,增加新的具体观察者无须修改原有系统代码,
在具体观察者与观察目标之间不存在关联关系的情况下,
增加新的观察目标也很方便。
缺点:
(1) 如果一个观察目标对象有很多直接和间接观察者,将所有的观察者都通知到会花费很多时间。
(2) 如果在观察者和观察目标之间存在循环依赖,观察目标会触发它们之间进行循环调用,
可能导致系统崩溃。
(3) 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,
而仅仅只是知道观察目标发生了变化。
(4)Java中消息的通知默认是顺序执行的,一个观察者卡顿,会影响整体的执行效率;
例子:
1、编写观察者和被观测者抽象接口
/**
* desc: 观测者 抽象接口
* author:Bill
* date: 2018/1/31
*/
public interface Observer {
void update(int edition, float cost);
}
/**
* desc:被观察者 抽象接口
* author:Bill
* date: 2018/1/31
*/
public interface Observerable {
// 注册
void registerObserver(Observer o);
// 解绑
void removeObserver(Observer o);
// 通知观测者
void notifyObservers();
}
2、实现被观察者接口
/**
* desc: 被观测者
* author:Bill
* date: 2018/1/31
*/
public class MagazineData implements Observerable {
private List mObservers;
private int edition;
private float cost;
public MagazineData() {
mObservers = new ArrayList<>();
}
/**
* 注册观察者
*
* @param o
*/
@Override
public void registerObserver(Observer o) {
mObservers.add(o);
}
/**
* 移除观察者
*
* @param o
*/
@Override
public void removeObserver(Observer o) {
int i = mObservers.indexOf(o);
if (i >= 0) {
mObservers.remove(i);
}
}
/**
* 通知观察者
*/
@Override
public void notifyObservers() {
// 遍历观察者 发送通知
for (int i = 0; i < mObservers.size(); i++) {
Observer observer = mObservers.get(i);
observer.update(edition, cost);
}
}
public void setInformation(int edition, float cost) {
this.edition = edition;
this.cost = cost;
//信息更新完毕,通知所有观察者
notifyObservers();
}
}
3、实现观察者接口
/**
* desc: 具体的观察者 实现观察者抽象接口Observer
* author:Bill
* date: 2018/1/31
*/
public class Customer implements Observer {
private String name;
private int edition;
private float cost;
Customer(String name) {
this.name = name;
}
@Override
public void update(int edition, float cost) {
this.edition = edition;
this.cost = cost;
buy();
}
private void buy() {
System.out.println(name + "购买了第" + edition + "期的杂志,花费了" + cost + "元。");
}
}
4、编写测试类
public class Test {
public static void main(String[] args) {
//创建被观察者
MagazineData magazine = new MagazineData();
//创建三个不同的观察者
Observer customerA = new Customer("A");
Observer customerB = new Customer("B");
Observer customerC = new Customer("C");
//将观察者注册到被观察者中
magazine.registerObserver(customerA);
magazine.registerObserver(customerB);
magazine.registerObserver(customerC);
//更新被观察者中的数据,当数据更新后,会自动通知所有已注册的观察者
magazine.setInfomation(5, 12);
}
}
3、建造者模式
使用场景:
对象复杂,表现形式不一,而且大同小异。“大同”需要提高复用,“小异”需要控制创建对象过程(顺序,步骤等可能不同或者有依赖)。
模式定义:
分离解耦,提高复用:
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
控制构建过程,利于扩展:
它关注如何一步一步创建一个的复杂对象,具体建造者之间相互独立,利于扩展。
模式目的:
分离解耦,提高复用,控制创建过程
角色构成:
①Client:
客户端,指定复杂对象的具体建造者, 与指挥者Director交互来获取复杂对象。
②Product:
由多个不同组件组成的,多样化的复杂对象
③Builder:
抽象建造者,包含的内容:
一是buildXXX创建复杂对象个各个部件,
二是getResult返回构建之后的复杂对象,
三是isXXX样式的hook method(钩子方法)用来细化复杂对象的构建过程。
④ConcreteBuilder:
具体建造者,实现Builder,定义具体的构建过程并且返回结果。
⑤Director:指挥者,
针对抽象Builder编程,指挥建造者去建造复杂对象;它封装建造的步骤和顺序,
隔离客户端和构建过程。
优缺点:
缺点:
如果对象的表现形式太过于多样化,会增加builder的个数,系统比较庞大。
如果产品差异性很大,那么不适合用建造者模式。
优点:
见“目的”和“定义”
实例:
1.Product 产品类
/**
* 计算机抽象类
* product角色
* Created by Bill on 2017/12/16.
*/
public abstract class Computer {
private String mBoard;
private String mDisplay;
String mOS;
protected Computer() {
}
// 设置CPU核心数
public void setBoard(String board) {
mBoard = board;
}
// 设置内存
public void setDisplay(String display) {
mDisplay = display;
}
// 设置操作系统
public abstract void setOS();
@Override
public String toString() {
return " My Computer:[ mBoard == " + mBoard + " ,mDisplay == " + mDisplay + " ,mOS" + mOS + "]";
}
}
/**
* 具体的Computer类
* Created by Bill on 2017/12/16.
*/
public class MacBook extends Computer {
public MacBook() {
}
@Override
public void setOS() {
mOS = "Mac OS X 10.13";
}
}
2.Builder
/**
* 抽象Builder类
* Created by Bill on 2017/12/16.
*/
public abstract class Builder {
public abstract void buildBoard(String board);
public abstract void buildDisplay(String display);
public abstract void buildOS();
public abstract Computer create();
}
/**
* 具体的Builder类
* Created by Bill on 2017/12/16.
*/
public class MacBookBuilder extends Builder {
private Computer mComputer = new MacBook();
@Override
public void buildBoard(String board) {
mComputer.setBoard(board);
}
@Override
public void buildDisplay(String display) {
mComputer.setDisplay(display);
}
@Override
public void buildOS() {
mComputer.setOS();
}
@Override
public Computer create() {
return mComputer;
}
}
3.Director类
/**
* Director类,负责构建computer
* Created by Bill on 2017/12/16.
*/
public class Director {
Builder mBuilder = null;
public Director(Builder builder) {
mBuilder = builder;
}
public void construct(String board, String display) {
mBuilder.buildBoard(board);
mBuilder.buildDisplay(display);
mBuilder.buildOS();
}
}
4.使用
/**
* Builder 模式
* 通过具体的MacBookBuilder来构建MacBook对像,而Director封装了构建复杂产品的过程,对外隐藏了细节。
* Builder和Director一起,将一个复杂对象的构建和表示分开,使得同样的构建过程 可以创建不同的对对象。
* Created by Bill on 2017/12/15.
*/
public class BuilderModel {
public static void main(String[] args) {
// 实例1
// 构建器
MacBookBuilder builder = new MacBookBuilder();
// Director 表示器
Director director = new Director(builder);
director.construct("英特尔主板", "Retina显示器");
Computer computer = builder.create();
System.out.println("Computer Info:" + computer.toString());
// 例子2
HuaShuoBuilder builder1 = new HuaShuoBuilder();
Director director1 = new Director(builder1);
director1.construct("华硕主板", "4K高端显示器");
Computer computer1 = builder1.create();
System.out.println("Computer Info:" + computer1.toString());
}
}
注意:
①可以省略Director指挥者,将创建对象的代码放在Builder里面,不过这么做不容易理解。
②与抽象工厂模式的不同之处在于:建造者模式侧重于一步步构造一个复杂对象。
4、简单工厂模式
使用场景:
对象的个数不是很多,并且创建对象的逻辑和业务不复杂
定义:
创建拥有同一父类的不同子类的对象的时候,引入工厂类,它可以根据参数的不同返回不同类的实例。
因为工厂方法是静态的,所以又叫“静态工厂方法模式”。
目的:
①封装创建过程,创建和使用分离
②引入配置文件灵活客户端
角色构成:
①Product:抽象产品类,客户端针对抽象编程,增加代码灵活性
②ConcreteProduct:具体产品类
③Factory:生产具体产品类的工厂
优缺点:
优点:
①封装创建过程,创建和使用分离
②引入配置文件增加客户端的灵活性
缺点:
①不利于扩展,不符合“开闭原则”
②若具体产品类很多而且创建过程复杂,初始化工作很多,则造成工厂的职责过重,并且过多的if..else逻辑判断影响效率。
③使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构
实例:
①创建三种不同种类的图形并且展示(圆形,矩形,棱形)
②读取数据库创建三种不同种类的表格并且展示(柱状,饼状,折线)
若创建产品和展示产品的代码放在一个类中,则:
职责过重
不符合开闭原则
客户端通过new创建对象和使用对象耦合
过多的if判断影响效率
/**
* 具体工厂类
* 也可以给某个产品设置自己的工厂类 各司其职
* Created by Bill on 2017/12/23.
*/
public class ConcreateFactory extends Factory {
/**
* 静态方法
*
* @return
*/
@Override
public Product createProduct() {
// return new ConcreteProductA();
return new ConcreteProductB();
}
/**
* 需要什么对爱那个 传入那个对象的类型即可
*
* @param tClass
* @param
* @return
*/
@Override
public T createProduct(Class tClass) {
Product p = null;
try {
p = (Product) Class.forName(tClass.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return (T) p;
}
}
package com.zhao.bill.designmodel.factorymodel.one;
/**
* 具体的产品A
* Created by Bill on 2017/12/23.
*/
public class ConcreteProductA extends Product {
@Override
public void method() {
System.out.println("我是具体的产品A");
}
}
package com.zhao.bill.designmodel.factorymodel.one;
/**
* 抽象工厂类
* Created by Bill on 2017/12/23.
*/
public abstract class Factory {
/**
* 抽象工厂方法
*
* @return
*/
public abstract T createProduct(Class tClass);
public abstract Product createProduct();
}
package com.zhao.bill.designmodel.factorymodel.one;
/**
* 抽象产品类
* Created by Bill on 2017/12/23.
*/
public abstract class Product {
/**
* 抽象方法
*/
public abstract void method();
}
注意:
①在使用时,首先需要对产品类进行重构,不能设计一个包罗万象的产品类,
而需根据实际情况设计一个产品层次结构,
公共的抽取到父类,个性化的生命抽象方法以供不同的具体产品类来实现。
②可以省去工厂类,将工厂方法放在抽象产品类中。
5.原型模式
*
使用场景:
* 当创建的多个对象有很多相同点,为了减少客户端的工作量,可以使用原型模式
*
*
定义:
* 将原型对象复制一份给新对象使用。
*
*
分类:
* 深克隆:
* 原型类和原型类的引用类型必须实现序列化Serializable接口,复制值类型,引用类型,对象本身。
* 浅克隆:
* 使用java提供的Clonable接口即可,只复制值类型和对象本身,对于引用类型只复制地址值。
*
角色:
* Clonable: 抽象原型类,java提供的浅克隆接口
* Serializable: 抽象原型类,深克隆需要实现的序列化接口
* ProteType: 具体原型类,提供克隆方法。
*
*
优缺点:
* 优点:
* 创建一个和已创建对象类似的对象提高创建效率。
* 相对于工厂方法模式,原型模式简化了创建结构。
* 缺点:
* 如果一个产品类嵌套很多引用类型,那么深克隆将会很麻烦,每层都必须支持深克隆,实现Serielizable。
*
*
注意:
* ①Serailizable和Clonable接口是标记接口,没有方法,实现此类接口是为了告诉jvm实现类具有某种功能。
②原型管理器:将原型类对象添加到管理器的map集合中,对外提供公共方法将克隆的对象返回给客户端,
也可以根据先有的已经改变值的对象克隆一个。
实例:
①周报:
周报除了内容不一样,其他所有的都一样;
②客户端中javaBean对象,克隆一份使用的情况也是很多
/**
* 文档类型
* WordDocument扮演的是concreatePrototype角色(具体的原型类)
* 而cloneable是代表的prototype角色(抽象类或者接口,声明具备clone能力)
* Created by Bill on 2017/12/20.
*/
public class WordDocument implements Cloneable {
private String mText;
private ArrayList mImages = new ArrayList();
public WordDocument() {
System.out.println("========== WordDocument 构造函数 ==========");
}
@Override
protected WordDocument clone() {
WordDocument document = null;
try {
document = (WordDocument) super.clone();
document.mText = this.mText;
// 1.浅拷贝
// document.mImages = this.mImages;
// 2.深拷贝 拷贝对象时,对于引用形的字段,也采用拷贝的形式 而不是单纯的引用
document.mImages = (ArrayList) this.mImages.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return document;
}
public void showDocu() {
System.out.println("========== WordDocument start ==========");
System.out.println("text: " + mText);
System.out.println("image list: ");
for (String image : mImages) {
System.out.println("image name: " + image);
}
System.out.println("========== WordDocument end ==========");
}
public String getText() {
return mText;
}
public void setText(String text) {
mText = text;
}
public ArrayList getImages() {
return mImages;
}
public void setImages(String images) {
this.mImages.add(images);
}
}
/**
* 客户端
* 浅拷贝:副文档的字段引用了原始文档的字段; 其中任何一个的修改,都会对另一个产生影响;
* 深拷贝:拷贝对象时,对于引用形的字段,也采用拷贝的形式 而不是单纯的引用
* Created by Bill on 2017/12/20.
*/
public class Client {
public static void main(String[] args) {
/* // 1、浅拷贝
// 构建对象
WordDocument originDocument = new WordDocument();
originDocument.setText("这是一篇文档");
originDocument.setImages("图片1");
originDocument.setImages("图片2");
originDocument.setImages("图片3");
originDocument.showDocu();
// 以原始文档为对象 拷贝一份
WordDocument cloneDocument1 = originDocument.clone();
originDocument.showDocu();
// 修改备份文档 不会影响原始文档
cloneDocument1.setText("这是修改过的文档2");
//cloneDocument1.setImages("哈哈.jpg");
cloneDocument1.showDocu();
originDocument.showDocu();*/
// 2.深拷贝 拷贝对象时,对于引用形的字段,也采用拷贝的形式 而不是单纯的引用
WordDocument originDocument = new WordDocument();
originDocument.setText("这是一篇文档");
originDocument.setImages("图片1");
originDocument.setImages("图片2");
originDocument.setImages("图片3");
originDocument.showDocu();
// 以原始文档为对象 拷贝一份
WordDocument cloneDocument1 = originDocument.clone();
originDocument.showDocu();
// 修改备份文档 不会影响原始文档
cloneDocument1.setText("这是修改过的文档2");
cloneDocument1.setImages("哈哈.jpg");
cloneDocument1.showDocu();
originDocument.showDocu();
}
}