设计模式综述
设计模式分为3大类
-
创建型模式
- 工厂方法
- 抽象工厂
- 单例
- 建造者
- 原型
-
结构型模式
- 适配器模式
- 装饰器模式
- 代理模式
- 外观模式
- 桥接模式
- 组合模式
- 享元模式
-
行为型模式
- 策略模式
- 模板方法模式
- 观察者模式
- 迭代子模式
- 责任链模式
- 命令模式
- 备忘录模式
- 状态模式
- 访问者模式
- 中介者模式
- 解释器模式
如果加上并发性模式以及线程池模式,就是5 + 7+ 11 + 2 = 25种
创建模式
01 工厂方法
在此之前,先说一下简单工厂模式(不属于23种设计模式之一) 举一个例子,工厂生成Xiaomi, Huawei手机。所以存在两条生产线
// 手机接口
public interface Phone {
void call();
}
// Xiaomi手机
public class XiaomiPhone implements Phone {
public void call() {
sout("xiaomi");
}
}
// HUAWEI手机
public class HuaweiPhone implements Phone {
public void call() {
sout("huawei");
}
}
工厂类
// 工厂1
public class ProduceFactoty1 {
public Phone produce(int type) {
if (type == 0) {
return new XiaomiPhone();
} else {
return new HuaweiPhone();
}
}
}
// 工厂2
public class ProduceFactory2 {
public Phone produceXiaomi() {
return new XiaomiPhone();
}
public Phone produceHuawei() {
return new HuaweiPhone();
}
}
// 工厂3,仅仅是把上文的方法改成静态的
public class ProduceFactory2 {
public static Phone produceXiaomi() {
return new XiaomiPhone();
}
public static Phone produceHuawei() {
return new HuaweiPhone();
}
}
上文的设计存在一个问题,假设现在想要生产Oppo手机了,就需要修改ProduceFactoty的代码,这违反了闭包原则,是一个很不友好的设计。于是就有了现在的工厂方法模式:创建一个工厂接口和创建多个工厂实现类
首先有一个生产方法接口
public interface Factory {
Phone produce();
}
// xiaomi 工厂
public XiaomiFactory implements Factory {
public Phone produce() {
return new XiaomiPhone();
}
}
// HUawei 工厂
public HuaweiFactory implements Factory {
public Phone produce() {
return new HuaweiPhone();
}
}
// 如果想添加其他品牌工厂,直接实现Factory接口即可
02抽象工厂模式
很容易与前面的工厂方法混淆,它属于工厂方法更进一步抽象
这里还是以上文的手机生产为例, xiaomi意识到了品牌重要性,于是有xiaomi高端机和低端机,huawei也是如此。于是有了以下类
// xiaomi高端机
class XiaomiHighPhone extends XiaomiPhone {
}
// xiaomi低端机
class XiaomiLowPhone extends XiaomiPhone {
}
// huawei高端机
class HuaweiHighPhone extends XiaomiPhone {
}
// huawei低端机
class HuaweiLowPhone extends XiaomiPhone {
}
显然,对于XiaomiHighPhone和XiaomiLowPhone 属于同一个品牌,而对于XiaomiHighPhone和HuaweiHighPhone是同一个品种,都属于高端机,如果继续用工厂方法,则需要建立4种工厂,我们想要是结果是只需要建立2种工厂,每一种工厂都可以都可以生产两种机型。
// 抽象工厂
interface class ProduceFactory {
XiaomiPhone produceXiaomiPhone();
HuaweiPhone produceHuaweiPhone();
}
// 具体工厂
class HighPhoneFactory {
public XiaomiPhone produceXiaomiPhone() {
return new XiaomiHighPhone();
}
public HuaweiPhone produceHuaweiPhone() {
return new HuaweiHighPhone();
}
}
class LowPhoneFactory {
public XiaomiPhone produceXiaomiPhone() {
return new XiaomiLowPhone();
}
public HuaweiPhone produceHuaweiPhone() {
return new HuaweiLowPhone();
}
}
下面我们来测试一下,生产一款小米高端手机
ProduceFactory factory = new HighPhoneFactory();
factory.produceXiaomiPhone();
03单例模式
单例模式就是我们所知的只需要一个单 instance的,经常会用到,这里直接略过, 贴一下经典代码
// 类加载线程互斥机制
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (null == instance) {
instance = SingletonFactory.instance;
}
return instance;
}
public static class SingletonFactory {
private static Singleton instance = new Singleton();
}
}
04建造者模式
个人理解的就是根据具体需求改变实例属性, 还是以手机为例
// 手机类
public class Phone {
private int size;
private String color;
public Phone(PhoneBuilder builder) {
this.size = builder.size();
this.color = builder.color();
}
public void setSize(int size) {
this.size = size;
}
public void setColor(String color) {
this.color = color;
}
// 默认5寸 红色
public static class PhoneBuilder {
public static PhoneBuilder defaultBuilder = new PhoneBuilder();
private int size = 5;
private String color = "red";
public PhoneBuilder setSize(int size) {
this.size = size;
return this;
}
public PhoneBuilder setColor(String color) {
this.color = color;
return this;
}
public Phone build() {
return new Phone(this);
}
}
}
PhoneBuilder.defaultBuilder.setSize(6).setColor("black").build();
05原型模型
基本思想就是将某一个实例作为原型,对其进行clone操作,得到新实例
public class Phone implements Cloneable {
public Phone clone() throws CloneNotSupportedException {
return (Phone) super.clone();
}
}
或许,可以测试下100W对象需要耗时多少,下面是三种生成实例的方法
for (int i = 0; i < 100_0000; i++) {
Phone p = new Phone();
}
Class<?> clazz = Phone.class;
for (int i = 0; i < 100_0000; i++) {
Phone p = clazz.newInstance();
}
Phone ori = new Phone();
for (int i = 0; i < 100_0000; i++) {
Phone p = ori.clone();
}
结构模式
06适配器模式
分为三种,这三种也是其他结构模式的基础
- 类的适配器模式
- 对象的适配器模式
- 接口的适配器模式
类的适配器模式
简而言之,就是存在类C, 需要将其等同于实现了接口 A
class Source {
void fun1() {
}
}
interface Target {
void fun1();
void fun2();
}
class Adapter extends Source implements Target {
public void fun2() {
}
}
例子
Target t = new Adapter();
t.fun1();
对象的适配器模式
思路与前面相同,但不再继承类,而是添加一个类成员变量
class Wrapper implements Target {
private Source source;
public Wrapper(Source source) {
this.source = source;
}
void fun1() {
source.fun1();
}
void fun2() {
}
}
接口的适配器模式
如果一个接口存在多个方法,而我们又不想全部实现,只想实现部分方法时。Netty有大量代码就是这样设计的
// 接口同上
// 抽象接口
public abstract class AbstractWrapper implements Target {
void fun1() {}
void fun2() {}
}
//
class Wrap1 extends AbstractWrapper {
void fun1() {
}
}
class Wrap2 extends AbstractWrapper {
void fun2() {
}
}
07装饰模式
当需要扩展一个类功能,同时也能随时撤销该类的功能
// 功能接口
interface Funcable {
void fun1();
}
// 原有类
class Source implements Funcable {
void fun1() {
}
}
// 装饰器
class Decorator implements Funcable {
private Source source;
Decorator(Source s) {
this.source = s;
}
void fun1() {
befor();
source.fun1();
after();
}
}
测试一下
Source source = new Source();
Decorator d = new Decorator(source);
d.fun1();
08代理模式
代理模式分为动态代理和静态代理,这里用静态代理说明,可以看出代码和上文的装饰器模式很像。
// 功能接口
interface Funcable {
void fun1();
}
// 原有类
class Source implements Funcable {
void fun1() {
}
}
// 代理类
class Proxy implements Funcable {
private Source source;
public Proxy() {
this.source = new Source();
}
public void fun1() {
before();
source.fun1();
after();
}
}
可以看到,装饰器模式唯一不同的就在于,Source变成了陌生类
Proxy p = new Proxy();
p.fun1();
09外观模式Facade
主要是为了解决类依赖问题,将他们的依赖关系放到Facade类中 举一个例子,存在一个http server要启动,可能需要初始化缓存模块,session模块, 大文件模块,用户不清楚他们之间的启动顺序,或者说依赖关系。 这时可以建立Facade类
class HttpServer {
void start() {
Cache.start();
Session.Start();
BigFile.start();
}
}
10桥接模式
我理解成类的具体实现可以单独分开。这里用数据库举一个例子
// 先统一定义数据库操作接口
public interface DBDriver {
void update();
}
// 数据库Mysql
public class MysqlDriver implements DBDriver {
void update() {
}
}
// 数据库Oracle
public class OracleDriver implements DBDriver {
void update() {
}
}
// 桥
public abstract class Bridge {
private DBDriver driver;
void update() {}
// getter
// setter
}
// 具体实现
public MyBridge extends Bridge {
void update() {
getDriver().update();
}
}
// 使用mysql
MyBridge myb = new MyBridge();
myb.setDriver(new MysqlDriver());
// 使用Oracle
myb.setDriver(new OracleDriver())
11组合模式
处理部分整体,典型就是树节点
class TreeNode {
int value;
TreeNode left;
TreeNode right;
}
class Tree {
private TreeNode root;
}
12享元模式
主要目的是实现对象的共享,这里以数据库连接池为例
public class DBConnPool {
List<DBConn> pool = new ArrayList<>();
public DBConnPool() {
for (int i = 0; i < size; i++) {
DBConn conn = new DBConn(...);
pool.add(pool);
}
}
public DBConn getConn() {
if (pool.size() > 0) {
DBConn conn = pool.get(0);
pool.remove(conn);
return conn;
}
return 0;
}
}
关系模式
- 父子类关系
- 策略模式
- 模板方法模式
- 两个独立类之间
- 观察者模式
- 迭代子模式
- 责任链模式
- 类的状态
- 备忘录模式
- 状态模式
- 通过中间件
- 访问者模式
- 中介者模式
- 解释器模式
13策略模式
策略模式定义了各种策略,用户自己选择用哪一个策略。或者应该说,就是interface的具体应用
interface Calculator {
int calculate(int num);
}
class Add implements Calculator {
public int calculate(int num) {
return num + 1;
}
}
class Reduce implements Calculator {
public int calculate(int num) {
return num - 1;
}
}
测试一下
Calculator cal = new Add();
cal.calculate(7);
14模板方法
这个写过框架的基本都会用到,netty中大量的也是这样的用法。这里以http server为例. 可以说,就是abstract类具体应用
abstract class AbstractController {
public void service(Request req, Response resp) {
doGet(Request req, Response resp);
}
abstrac void doGet();
}
class Test1Controller extend AbstractController {
public void doGet(Request req, Response resp) {
}
}
class Test2Controller extend AbstractController {
public void doGet(Request req, Response resp) {
}
}
测试代码
AbstractController t = new Test1Controller();
t.service();
观察者模式
不解释了,直接上代码
// 定义一个监狱犯罪人接口
interface Eviler {
// 收到消息
void rcv(String s);
}
// 定义偷窃罪人
class TouqieEviler implements Eviler {
public void rcv (String s) {
sout("touqie" + s);
}
}
// 定义打劫罪犯罪人
class DajieEviler implements Eviler {
public void rcv (String s) {
sout("dajie" + s);
}
}
//定义一个警察
class Police {
//增加监控的犯罪人
Set<Eviler> set = new HashSet<>();
void addEviler(Eviler e) {
set.add(e);
}
void deleteEvile(Eviler e) {
set.remove(e);
}
// 通知所有被监控人
void notifyAll() {
for (Eviler e : set) {
e.rcv();
}
}
}
迭代子模式
简而言之,就是实现一个迭代器,java中的集合对象都用到了
// 先定义一个迭代器接口
interface Iterator {
Object previous(); //前一个元素
Object next(); // 后一个元素
boolean hasNext(); // 是否有后面的元素
Object first(); // 第一个元素
}
// 再定义一个集合接口
interface Collection {
Iterator iterator();
Object get(int i); // 得到集合内的元素
int size(); // 集合大小
}
//定义一个自己的迭代器
class MyIterator implements Iterator {
private Collection col;
private int pos = -1;
public MyIterator(Collletion c) {
this.col = c;
}
public Object previous() {
if (pos > 0) {
pos--;
}
return col.get(pos);
}
public Object next() {
if (pos < col.size() - 1) {
pos++;
}
return col.get(pos);
}
public boolean hasNext() {
return pos < col.size() - 1;
}
public Object first() {
pos = 0;
return col.get(pos);
}
}
// 最后定义一个自己的集合类
class MyCollection implements Collection {
public Obejct[] objs = {"zhuj", 11, 'h', 12.3}
Iterator iterator;
public Iterator iterator() {
/* 特别注意,
这个是完全错误的写法。
如果thread1 执行next, thread2执行previous, 那还怎么遍历?
遍历有个基本条件,每次都是是从开头到结尾,而不是只有部分元素
if (null == iterator) {
iterator = new MyIterator(this);
}
return iterator;
*/
return new MyIterator(this);
}
public Object get(int i) {
return objs[i];
}
public int size() {
return objs.length;
}
}
定义一个测试
Collection col = new MyCollection();
Iterator it = col.iterator();
while (it.hasNext()) {
sout(it.next());
}
我不是很清楚迭代器在集合内部有什么特殊优势,这里从网上看到一个例子,说一下在某些情况下的为什么要用迭代器实现效果非常好。例如,我们要得到前100个斐波那契数列的值。所以有以下写法
main() {
for (int i = 1; i < 100; i++) {
sout(fibonacci(i));
}
}
private int fibonacci(int num) {
if (num <= 0) {
throw new UnsupportedOperationException("斐波那契数列不允许输入非自然数");
}
int ret;
if (num == 1) {
ret = 1;
} else if (num == 2) {
ret = 1;
} else {
ret = fibonacci(num - 1) + fibonacci(num - 2);
}
return ret;
}
很显然,这样输出的话,量会很大,基本把栈搞崩,所以,改成循环的用法
main() {
for (int i = 1; i < 100; i++) {
sout(fibonacciLoop(i));
}
}
private int fibonacciLoop(int num) {
if (num <= 0) {
throw new UnsupportedOperationException("斐波那契数列不允许输入非自然数");
}
if (num == 1) {
return 1;
}
if (num == 2) {
return 1;
}
int f1 = 1;
int f2 = 1;
int f3 = f1 + f2;
int i = 2;
while (i++ < num) {
f3 = f2 + f1;
f1 = f2;
f2 = f3;
}
return f3;
}
这个方法也存在一个问题,因为每一次计算都要循环计算之前已经计算过的值,这在计算机中,是很忌讳的用法。所以,接下来,用一个迭代器。这样就存储的之前的结果,也避免了大量的重复计算。
main() {
Iterator it = new FibonacciIterator(100);
while(it.hasNext()) {
sout(it.next())
}
}
class FibonacciIterator implements Iterator {
int f1 = 1;
int f2 = 1;
int f3 = 0;
int count;
int index = 1;
public FibonacciIterator(int val) {
this.count = val;
}
public boolean hasNext() {
return index < count;
}
public int next() {
index++;
if (index == 1) {
return f1;
}
if (index == 2) {
return f2;
}
f3 = f2 + f1;
f1 = f2;
f2 = f3;
return f3;
}
}
17责任链模式
字面意思,存在多个对象,每个对象有对下一个对象的引用,这样看起来就像是一条链子,一直到存在某个对象(实例)处理请求。
class Handler {
private Handler handler;
private String name;
public Handler(String s) {
this.name = s;
}
public void setHandler(Handler h) {
this.handler = h;
}
public void handle() {
sout(name + "handle it");
if (handler != null) {
handler.handle();
}
}
}
void main() {
Handler h1 = new Handler("h1");
Handler h2 = new Handler("h2");
Handler h3 = new Handler("h3");
h1.setHandler(h2);
h2.setHandler(h3);
h1.handle();
}
18命令模式
// 新建一个命令接口
interface Cmd {
void exec();
}
// 具体的命令, 关机命令
class ShutdwonCmd implements Cmd {
private Pc pc;
public ShutdwonCmd(Pc pc) {
this.pc = pc;
}
public void exec() {
pc.act_shutdown();
}
}
// 执行者,执行命令的类
class Pc {
public void act_startup() {
}
public void act_shutdown() {
sout("执行关机。。。");
}
}
// 调用命令的类,或者可以分配命令的类
public class Invoker {
private Cmd cmd;
public Invoker(Cmd cmd) {
this.cmd = cmd;
}
public void act() {
cmd.exec();
}
}
void main() {
// 最终执行命令的对象
Pc pc = new Pc();
// 要求被执行的命令
Cmd cmd = new ShutdownCmd(pc);
// 发布命令
Invoker invoker = new Invoker(cmd));
invoker.act();
}
19备忘录模式
主要目的就是备忘录,也就是保存某对象的状态,必要时恢复。也就是说明,本质就是备份。 我们以一本书为例
// 先创建一个快照类,这个类用来保存当前书籍的内容,只读不写
class BookSnapshot {
static int CountFlag = 1;
int id;
String val;
public BookSnapshot(String val) {
this.id = CountFlag++;
this.val = val;
}
public getValue() {
return this.val;
}
public int getId() {
return id;
}
}
// 一本书
class Book {
private String content = "";
// 书本写入新的内容
public void writeContent(String s) {
this.content += s;
}
// 得到当前书本内容
public String getContent() {
return this.content;
}
// 创建快照
public BookSnapshot createSnapshot() {
return new BookSnapshot(content);
}
// 根据快照恢复书籍的内容
public restore(BookSnapshot shot) {
this.content = shot.getValue();
}
}
// 快照管理类,也就是备忘录
class BookManager {
Map<Integer, BookSnapshot> map = new HashMap();
//存储新的快照
public void addBookSnapshot(BookSnapshot shot) {
map.put(shot.getId(). shot);
}
//拿出旧的快照
public BookSnapshot getBookSnapshot(int id) {
return map.get(id);
}
}
到了这一步,可以测试一下
//先写一本书
Book book = new Book();
BookManager bm = new BookManager();
// 写入内容
book.writeContent("123");
// 创建快照
BookSnapshot shot1 = book.createSnapshot();
bm.addBookSnapshot(shot1);
// 再次写入内容
book.writeContent("1we");
// 不想写了,恢复过去的内容
book.restore(bm.getBookSnapshot(1));
20状态模式
简而言之,不同的状态得到不同的行为, 这里直接以QQ为例
class QQ {
static int online = 1;
static int offline = 2;
private int state;
public void setState(int s) {
this.state = s;
}
public int getState() {
return this.state;
}
public void online() {
sout("我在线");
}
public voud offline() {
sout("我离线了");
}
}
class Pc {
private QQ qq;
public Pc(QQ qq) {
this.qq = qq;
}
// 打开qq
public openQQ() {
if (qq.getState == QQ.online) {
qq.online();
} else {
qq.offline();
}
}
}
void main() {
QQ qq = new QQ();
Pc pc = new Pc(qq);
qq.setState(1);
pc.openQQ();
qq.setState(2);
pc.openQQ();
}
21访问者模式
这个有点看不明白使用场景了.
// 先定义一个访问者接口
interface Visitor {
void visit(Subject sub);
}
// 具体的访问者,假设是记者
class ReportVisitor implements VIsitor {
public void visite(Subject sub) {
sout("ReportVisitor 访问了 " + sub);
sout("得到了许多信息:" + sub.getInfo());
}
}
// 被访问者接口
interface Subject {
void accept(Visitor vistor);
String getInfo();
}
// 具体的被访问者,假设是一名作家
class WriterSubject implements Subject {
public void accept(Visitor visite) {
visite.visite(this);
}
public String getInfo() {
return "我的最新书籍是《》";
}
}
测试代码
Visitor vistor = new ReportVisitor();
WriterSubject writer = new WriterSubject();
writer.accept(vistor); //接受采访
22中介者模式
中介模式相对好理解一些,存在两个类相互关联,也就是相互包含,改变其中任何一个都可以可能改动全部,这个时候引入中介会降低耦合,以二手房地产为例
// 先建立客户接口
interface Buziness {
void action();
}
// 二手房卖家
class Seller implements Buziness {
public void action() {
}
}
// 买家
class Buyer implements Buziness {
public void action() {
}
}
// 中介
class Mediator {
private Seller seller;
private Buyer buyer;
...
public void work() {
seller.action();
buyer.action();
}
}
23解释器模式
一般用的很少,除非编译器开发, pass