1.设计模式的分类
⑴创建型
创建型(Creational)模式:将对象的部分创建工作延迟到子类或者其他对象,从而应对需求变化为对象创建时具体类型实现引来的冲击。
简单工厂模式(Simple Factory)
工厂方法模式(Factory Method)
抽象工厂模式(Abstract Factory)
创建者模式(Builder)
原型模式(Prototype)
单例模式(Singleton)
⑵结构型
结构型(Structural)模式:结构型模式是为解决怎样组装现有的类,设计它们的交互方式,从而达到实现一定的功能目的。结构型模式包容了对很多问题的解决。例如:扩展性(外观、组成、代理、装饰)、封装(适配器、桥接)。
外观模式/门面模式(Facade门面模式)
适配器模式(Adapter)
代理模式(Proxy)
装饰模式(Decorator)
桥梁模式/桥接模式(Bridge)
组合模式(Composite)
享元模式(Flyweight)
⑶行为型
行为型(Behavioral)模式:通过类继承或者对象组合划分类与对象间的职责,从而应对需求变化对多个交互的对象带来的冲击。
模板方法模式(Template Method)
观察者模式(Observer)
状态模式(State)
策略模式(Strategy)
职责链模式(Chain of Responsibility)
命令模式(Command)
访问者模式(Visitor)
调停者模式(Mediator)
备忘录模式(Memento)
迭代器模式(Iterator)
解释器模式(Interpreter)
三者关系
•创建型模式为其他两种模式使用提供了环境。
•结构型模式侧重于接口的使用,它做的一切工作都是对象或是类之间的交互,提供一个门。
•行为型模式顾名思义,侧重于具体行为,所以概念中才会出现职责分配和算法通信等内容。
2.常见的设计模式
⑴单例模式
①定义
确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例.
②使用场景
确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多资源,或者某个类型的对象只应该有且只有一个.
关键点:
(1)私有构造函数;
(2)通过一个静态方法或者枚举返回单例类对象;
(3)确保单例类的对象有且只有一个,尤其是多线程环境下;
(4)确保単例类对象在反序列化时不会重新构建对象.
③实现方法
❶饿汉式:一上来就把对象创建好,不管能否用的到
public class SingleTon {
private static SingleTon singleTon = new SingleTon();
private SingleTon() {
}
public static SingleTon getInstance() {
return singleTon;
}
}
❷懒汉式:需要的时候才会去创建对象
//有问题: 线程不安全,因为多线程情况下回创建多个对象
//优势: 延迟了对象的实例化
//多线程 :线程A,线程B
//多CPU,多个线程可以同时执行,如果是单CPU,同一时间只有一个线程可以执行
//单CPU,多线程看着是同时执行,实际上不是,其实是各个线程在抢时间片
public class SingleTon2 {
private static SingleTon2 singleTon2 = null;
private SingleTon2() {
}
//线程A,线程B
//1.线程A先执行
public static SingleTon2 getInstance() {
//2.线程A进来了,发现对象为null
//4.线程B进来后因为刚才A没有来的及new对象,所以对象还是null,B也的进去new
if (singleTon2 == null) {
//3.线程A进来new对象,假设A进来后还没有来得及new对象,时间片被线程B抢去了
//5.B也进来了
singleTon2 = new SingleTon2();
}
return singleTon2;
}
}
❸Double Check Lock(DCL)
public class SingleTon3 {
int a;
// private static SingleTon3 singleTon3=null;
private volatile static SingleTon3 singleTon3 = null;
private SingleTon3() {
a = 10;
}
//写法1:没有问题
//缺点:synchronized效率低
// public static synchronized SingleTon3 getInstance(){
// if (singleTon3 == null){
// singleTon3 = new SingleTon3();
// }
//
// return singleTon3;
// }
//写法2
///已经优秀很多了
//还有问题
//线程A
//线程B
public static SingleTon3 getInstance() {
//第一次避免不必要的同步;
if (singleTon3 == null) {
synchronized (SingleTon3.class) {
//第二次是在非空的情况下创建实例.
if (singleTon3 == null) {
//new对象不是原子操作
// (1).给Singleton实例分配内存;
// (2).调用Singleton的构造,初始化成员字段;
// (3).将sInstance指向分配的内存空间(sInstance就不是null了).
//因为java内存模型 步骤有可能是1,2,3,也有能是1,3,2的走法
//如果是1,2,3是没有问题的,但是如果是1,3,2,就有问题
//解决:对象申明的时候添加volatile关键字
singleTon3 = new SingleTon3();
}
}
}
return singleTon3;
}
}
❹静态内部类単例模式
//第一次加载Singleton时并不会实例化sInstance,只有在第一次调用getInstance()时才会实例化sInstance,
//这种方式不仅能够确保线程安全,也能保证単例对象的唯一性,同时也延迟了単例的实例化,推荐使用.
public class SingleTon4 {
private SingleTon4() {
}
public static SingleTon4 getInstance() {
return SingleTonHolder.singleTon4;
}
private static class SingleTonHolder {
static SingleTon4 singleTon4 = new SingleTon4();
}
}
⑵观察者模式
①定义
•定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新.
•观察者模式是一个使用率非常高的模式,它最常用的地方是GUI系统,订阅--发布系统,它最重要的作用就是解耦,将被观察者和观察者解耦,使得他们之间的依赖性更小.如应用UI具有易变性,但业务逻辑基本变化不大,此时GUI系统需要一套机制来应对,使得UI层与具体业务逻辑解耦,观察者模式此时就派上用场了.
②使用场景
•关联行为场景,需要注意的是,关联行为是可以拆分的,不是组合关系;
•事件多级触发场景;
•跨系统的消息交换场景,如消息队列,事件总线的处理机制.
③实现方法
private void obser() {
honor = new Honor();
Coder coder = new Coder();
LittleStudent littleStudent = new LittleStudent();
//建立观察关系
honor.addObserver(coder);
honor.addObserver(littleStudent);
}
④总结
•观察者模式主要作用就是对象解耦,将观察者和被观察者完全隔离,只依赖Observer和Observable抽象.
优点:
•观察者和被观察者之间是抽象耦合,应对业务变化;
•增强系统灵活性,可扩展性.
缺点:
•应用观察者模式时需要考虑一下开发效率和运行效率问题,程序中包括一个被观察者,多个观察者,开发和调试等内容会比较复杂,而且在Java中消息的通知默认是顺序执行,一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般考虑采用异步的方式.
⑶工厂模式
①定义
定义一个用于创建对象的接口,由子类决定实例化哪个类.(Factory Pattern)
②使用场景
•在任何需要生成复杂对象的地方,都可以使用工程方法模式,用new就可以完成创建的对象无需使用工厂方法模式.
•这里的角色分为四类:
1.抽象工厂,工厂方法模式的核心;
2.具体工厂,实现了具体的业务逻辑;
3.抽象产品,工厂方法模式创建的产品父类;
4.具体产品,实现抽象产品的某个具体产品对象.
③总结
工厂方法模式是一个很好的设计模式,但缺点也是难以避免的,每次为工厂方法模式添加新的产品时,就要编写一个产品类,同时还要引入抽象层,这必然导致类结构复杂化,所以在某些比较简单的情况下,是否使用工厂方法模式就需要衡量了.
⑷模板方法
①定义
•定义一个操作中的算法框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤.
•在面向对象开发过程中,通常会遇到这样的一个问题,我们知道一个算法所需要的关键步骤,并确定了这些步骤的执行顺序,但是,某些步骤的具体实现是未知的,或者说某些步骤的实现会随着环境的变化而改变,例如执行程序的流程大致如下:
1.检查代码的正确性;
2.链接相关的类库;
3.编译相关代码;
4.执行程序.
•对于不同程序设计语言,上述4个步骤都是不一样的,但是,它们的执行流程是固定的,这类问题的解决方案就是模板方法模式.
②使用场景
1.多个子类有公有的方法,并且逻辑基本相同时;
2.重要,复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现;
3.重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数约束其行为.
③实现方法
private void template() {
new Xiaobai().startUp();
new MaNong().startUp();
new ArmyComputer().startUp();
}
④总结
•模板方法模式用4个字概括:流程封装;也就是把某个固定流程封装到final函数中,并让子类定制这个流程中某个或者某些步骤;
•优点:
1.封装不变部分,扩展可变部分;
2.提取公共部分代码,便于维护;
•缺点:
模板方法会带来代码阅读的难度,会让用户觉得难以理解.
⑸构建者模式 (Builder模式)
①定义
•将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示.
•Builder模式是一步一步创建一个复杂对象的创建型模式,它允许用户不知道内部构建细节的情况下,可以更精细的控制对象的构造流程.
•该模式是为了将构建该对象的过程和它的部件解耦,使得构建过程和部件的表示隔离开来.
②使用场景
1.相同的方法,不同的执行顺序,产生不同的事件结果时;
2.多个部件都可以装配到一个对象中,但产生的运行结果又不相同时;
3.产品类非常复杂,或者产品类中的调用顺序不同产生了不同的作用;
4.当初始化一个对象特别复杂,如参数多,且很多参数都有默认值时.
③实现方法
public abstract class Builder {
public abstract Builder builderBoard(String board);
public abstract Builder builderDisplay(String display);
public abstract Builder builderOs();
public abstract Computer create();
}
public abstract class Computer {
protected String board;
protected String display;
protected String os;
public void setBoard(String board) {
this.board = board;
}
public void setDisplay(String display) {
this.display = display;
}
public abstract void setOs();
@Override
public String toString() {
return "Computer{" +
"board='" + board + '\'' +
", display='" + display + '\'' +
", os='" + os + '\'' +
'}';
}
}
public class MacBook extends Computer {
@Override
public void setOs() {
os = "mac 系统";
}
}
public class HuaWeiBook extends Computer {
@Override
public void setOs() {
os = "Window 98";
}
}
public class MacBulider extends Builder {
MacBook macBook = new MacBook();
@Override
public Builder builderBoard(String board) {
macBook.setBoard(board);
return this;
}
@Override
public Builder builderDisplay(String display) {
macBook.setDisplay(display);
return this;
}
@Override
public Builder builderOs() {
macBook.setOs();
return this;
}
@Override
public Computer create() {
return macBook;
}
}
public class HuaWeiBulider extends Builder {
private HuaWeiBook huaWeiBook = new HuaWeiBook();
@Override
public Builder builderBoard(String board) {
huaWeiBook.setBoard(board);
return this;
}
@Override
public Builder builderDisplay(String display) {
huaWeiBook.setDisplay(display);
return this;
}
@Override
public Builder builderOs() {
huaWeiBook.setOs();
return this;
}
@Override
public Computer create() {
return huaWeiBook;
}
}
private void build() {
Computer mac = new MacBulider()
.builderBoard("CPU")
.builderDisplay("显示器")
.builderOs()
.create();
Log.e(TAG, "build: " + mac);
Computer huawei = new HuaWeiBulider()
.builderBoard("i9")
.builderDisplay("不知道")
.builderOs()
.create();
Log.e(TAG, "build: " + huawei);
}
3.设计模式的六大原则
单一职责原则
• 一个类中应该是一组相关性很高的函数,数据的封装.但因为划分界限并不是总是那么清晰,很多时候要依赖个人的经验来界定.
开闭原则
•软件中的对象(类,模块,函数等)应该对于扩展是开放的,但是,对于修改时封闭的.而遵循开闭原则的重要手段应该是通过抽象.
里氏替换原则
•所有引用基类的地方必须能透明地使用其子类的对象.(父类可以出现的地方,子类就一定可以出现,总结为:抽象)
依赖倒置原则
• 指代了一种特定的解耦形式,使得高层次模块不依赖于低层次模块的实现细节,依赖模块被颠倒了.
接口隔离原则
•客户端不应该依赖它不需要的接口;另一定义:类间的依赖关系应该建立在最小的接口上.即将庞大.臃肿的接口拆分成更小的和更具体的接口,客户只需要知道他们感兴趣的方法.
迪米特原则
•一个对象应该对其他对象有最少的了解.即一个类应该对自己需要耦合或者调用的类知道得最少。
类与类之间关系越密切,耦合度就越大,当一个类发生改变时,对另一个的影响也越大。
网友评论