设计模式:门面模式(Facade Pattern)

定义

动态地给一个对象添加一些额外的职责。就增加功能来说,它相比生成子类更为灵活

类型

结构类模式

类图

Facade Pattern

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class ClassA {
public void doSomethingA(){
//业务逻辑
}
}

public class ClassB {
public void doSomethingB() {
//业务逻辑
}
}

public class ClassC {
public void doSomethingC() {
//业务逻辑
}
}

public class Facade {
private ClassA a = new ClassA();
private ClassB b = new ClassB();
private ClassC c = new ClassC();

public void methodA() {
this.a.doSomethingA();
}
public void methodB() {
this.b.doSomethingB();
}
public void methodC() {
this.c.doSomethingC();
}
}

优点

  • 减少系统的互相依赖
  • 提高了灵活性
  • 提高安全性

缺点

不符合开闭原则

使用场景

  • 为一个复杂的模块或子系统提供一个供外界访问的接口
  • 子系统相对独立——外界对子系统的访问只要黑箱操作即可
  • 预防低水平人员带来的风险扩散

评论和共享

设计模式:享元模式模式(Flyweight Pattern)

定义

动态地给一个对象添加一些额外的职责。就增加功能来说,它相比生成子类更为灵活

类型

结构类模式

类图

Flyweight Pattern

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public abstract class Flyweight{
//内部状态
private String intrinsic;
//外部状态
protected final String Extrinsic;
//享元角色必须接受外部状态
public Flyweight(String _Extrinsic){
this.Extrinsic = _Extrinsic;
}

public abstract void operate();

public String getIntrinsic() {
return intrinsic;
}

public void setIntrinsic(String intrinsic){
this.intrinsic = intrinsic;
}
}

public class ConcreteFlyweight1 extends Flyweight{
public ConcreteFlyweight1(String _Extrinsic){
super(_Extrinsic);
}
public void operate(){
//业务逻辑
}
}

public class ConcreteFlyweight2 extends Flyweight{
public ConcreteFlyweight2(String _Extrinsic){
super(_Extrinsic);
}
public void operate(){
//业务逻辑
}
}

public class FlyweightFactory{
private static HashMap<String, Flyweight> pool = new HashMap<String, Flyweight>();

//享元工厂
public static Flyweight getFlyweight(String Extrinsic){
Flyweight flyweight = null;

if(pool.containsKey(Extrinsic)){
flyweight = pool.get(Extrinsic);
}else{
flyweight = new ConcreteFlyweight1(Extrinsic);
pool.put(Extrinsic, flyweight);
}
return flyweight;
}
}

优点

大大减少应用程序创建的对象,减低程序内的占用,增强程序的性能

## 缺点
提高了系统的复杂性,需要分离出外部状态和内部状态。

使用场景

  • 系统中存在大量的相似对象
  • 细粒度的对象都具备较结晶的外部状态
  • 需要缓冲池的场景

评论和共享

定义

动态地给一个对象添加一些额外的职责。就增加功能来说,它相比生成子类更为灵活

类型

结构类模式

类图

Decorator Pattern

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public abstract class Component {
public abstract void operate();
}

public class ConcreteComponent extends Component {
@Override
public void operate() {
System.out.println("do Something");
}
}

public abstract class Decorator extends Component {
private Component component = null;

public Decorator(Component _component){
this.component = _component;
}

@Override
public void operate() {
this.component.operate();
}
}

public class ConcreteDecorator1 extends Decorator {
public ConcreteDecorator1(Component _component){
super(_component);
}
//定义自己的修饰方法
private void operate(){
System.out.println("method1 修饰");
}

public void operate() {
this.method1();
super.operate();
}
}

public class Client{
public static void main(String[] args){
Component component = new ConcreteComponent();
component = new ConcreteDecorator1(component);
component = new ConcreteDecorator2(component);
component.operate();
}
}

优点

  • 装饰类和被装饰类可以独立发展
  • 装饰模式是继承关系的代替方案
  • 装饰模式可以动态地扩展一个实现类的功能

## 缺点
多层的装饰是比较复杂的,尽量减少装饰类的数量,以便降低系统的复杂度。

评论和共享

定义

将对象组合成树型结构,以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

类型

结构类模式

类图

Composite Pattern

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public abstract class Component {
public void doSomething(){
//业务逻辑
}
}

public class Composite extends Component {
private ArrayList<Component> componentArrayList = new ArrayList<Component>();

public void add(Component component){
this.componentArrayList.add(component);
}

public void remove(Component component){
this.componentArrayList.remove(component);
}

public ArrayList<Component> getChildren() {
return this.componentArrayList;
}
}

public class Leaf extends Component {
public void doSomething() {

}
}

public class Client {
public static void main(String[] args){
Composite root = new Composite();
root.doSomething();

Composite branch = new Composite();
Leaf leaf = new Leaf();
root add(leaf);
}
}

优点

  • 高层模块调用简单
  • 节点自由增加

缺点

树叶和树枝使用时直接使用了实现类,在面向接口编程上是很不恰当的,与依赖倒置原则冲突。

## 使用场景

  • 维护和展示部分——整体关系的场景,比如树形菜单,文件和文件夹管理
  • 从一个整体中能够独立出部分模块或功能的场景

评论和共享

定义

将抽象和实现解耦,使得两者可以独立的变化

类型

结构类模式

类图

Bridge Pattern

角色

  • Abstraction——抽象化角色
  • Implementor——实现化角色
  • RefinedAbstraction——修正抽象化角色
  • ConcreteImplementor——具体实现化角色

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public interface Implementor {
public void doSomething();
public void doAnything();
}

public class ConcreteImplementor1 implements Implementor {
public void doSomething(){
//业务逻辑处理
}
public void doAnything() {
//业务逻辑处理
}
}
//ConcreteImplementor2 省略

public abstract class Abstraction {
private Implementor imp;
public Abstraction(Implementor _imp){
this.imp = _imp;
}
public void request() {
this.imp.doSomething();
}
public Implementor getImp(){
return imp;
}
}

public class RefinedAbstraction extends Abstraction {
public RefinedAbstraction(Implementor _imp){
super(_imp);
}
@Override
public void request(){
super.request();
super.getImp().doAnything();
}
}

public class Client {
public static void main(String[] args) {
Implementor imp = new ConcreteImplementor1();
Abstraction abs = new RefinedAbstraction(imp);
abs.request();
}
}

优点

  • 抽象和实现分离
  • 优秀的拓展能力
  • 实现细节对客户透明

使用场景

  • 不希望或不适用使用继承的场景
  • 接口或抽象类不稳定的场景
  • 重用性要求较高的场景

评论和共享

定义

为其他对象提供一种代理以控制对这个对象的访问

类型

结构类模式

类图

Proxy Pattern

角色

  • Subject——抽象主题角色
  • RealSubject——具体主题角色
  • Proxy——代理主题角色

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public interface Subject {
public void request();
}

public class RealSubject implements Subject {
public void request(){
//业务逻辑处理
}
}

public class Proxy implements Subject {
private Subject subject = null;

public Proxy() {
this.subject = new Proxy();
}

public Proxy(Object...object){

}

public void request() {
this.before();
this.subject.request();
this.after();
}

private void before() {

}

private void after() {

}
}

优点

  • 职责清晰
  • 高拓展性
  • 智能化

评论和共享

定义

讲一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够一起工作

类型

结构类模式

类图

Adapter Pattern

要素

  • Target——目标角色
  • Adapter——源角色
  • Adapter——适配器角色

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public interface Target {
public void request();
}

public class ConcreteTarget implements Target {
public void request() {
System.out.println("if you need any help, pls call me!");
}
}

public class Adaptee {
public void doSomething() {
System.out.println("I'm kind of busy, leave me alone, pls!")
}
}

public class Adapter extends Adaptee implements Target {
public void request() {
super.doSomething();
}
}

public class Client {
public static void main(String[] args) {
Target target = new ConcreteTarget();
target.request();

Target target2 = new Adapter();
target2.request();
}
}

优点

  • 可以让两个没有任何关系的类在一起运行。
  • 增加了类的透明性
  • 提高了类的复用度
  • 灵活性非常好

使用场景

在详细设计阶段不用考虑,而是解决正在复议的项目的问题

评论和共享

定义

当一个对象内在状态改变时允许其改变行为,这个对象看起来像是改变了其类。

类型

创建类模式

类图

State Pattern

要素

  • State——抽象状态角色

    接口抽象类,负责对象状态定义,并且封装环境角色以实现状态转换。

  • ConcreteState——具体状态角色

    每一个具体状态必须完成两个职责: 本状态的行为管理以及趋向状态处理,通俗地说,就是本状态下要做的事情,以及本状态如何过渡到其他状态

  • Context——环境角色

    定义客户端需要的接口,并且负责具体状态的切换。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public abstract class State {
protected Context context;

public void setContext(Context _context){
this.context = _context;
}

public abstract void handle1();

public abstract void handle2();
}

public class ConcreteState1 extends State {
@Override
public void handle1(){
super.context.setCurrectState(Context.STATE2);
super.context.handle2();
}
@Override
public void handle2(){

}
}

public class Context{
public final static State STATE1 = new ConcreteState1();
public final static State STATE2 = new ConcreteState2();

private State CurrentState();

public State getCurrentState() {
return CurrentState;
}

public void setCurrentState(State currentState) {
this.CurrentState = currentState;
this.CurrentState.setContext(this);
}

public void handle1(){
this.CurrentState.handle1();
}

public void handle2(){
this.CurrentState.handle2();
}
}

public class Client {
public static void main(String[] args){
Context context = new Context();
context.setCurrentState(new ConcreteState1);

context.handle1();
context.handle2();
}
}

优点

  • 结构清晰

    避免许多switch…case

  • 遵循设计原则

    很好地体现了开闭原则和单一职责原则

  • 封装性非常好

缺点

子类会太多,可以解决,比如在数据库中建立一个状态表,然后根据状态执行相应的操作。

使用场景

  • 行为随状态改变而改变的场景
  • 条件,分支判断语句的代替者

评论和共享

定义

给定一种语言,定义他的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中句子。

类型

行为类模式

类图

Interpreter Pattern

结构

  • 抽象解释器:声明一个所有具体表达式都要实现的抽象接口(或者抽象类),接口中主要是一个interpret()方法,称为解释操作。具体解释任务由它的各个实现类来完成,具体的解释器分别由终结符解释器TerminalExpression和非终结符解释器NonterminalExpression完成。
  • 终结符表达式:实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。终结符一半是文法中的运算单元,比如有一个简单的公式R=R1+R2,在里面R1和R2就是终结符,对应的解析R1和R2的解释器就是终结符表达式。
  • 非终结符表达式:文法中的每条规则对应于一个非终结符表达式,非终结符表达式一般是文法中的运算符或者其他关键字,比如公式R=R1+R2中,+就是非终结符,解析+的解释器就是一个非终结符表达式。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。
  • 环境角色:这个角色的任务一般是用来存放文法中各个终结符所对应的具体值,比如R=R1+R2,我们给R1赋值100,给R2赋值200。这些信息需要存放到环境角色中,很多情况下我们使用Map来充当环境角色就足够了。

实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Context {}  
abstract class Expression {
public abstract Object interpreter(Context ctx);
}
class TerminalExpression extends Expression {
public Object interpreter(Context ctx){
return null;
}
}
class NonterminalExpression extends Expression {
public NonterminalExpression(Expression...expressions){

}
public Object interpreter(Context ctx){
return null;
}
}
public class Client {
public static void main(String[] args){
String expression = "";
char[] charArray = expression.toCharArray();
Context ctx = new Context();
Stack<Expression> stack = new Stack<Expression>();
for(int i=0;i<charArray.length;i++){
//进行语法判断,递归调用
}
Expression exp = stack.pop();
exp.interpreter(ctx);
}
}

优点

解释器是一个简单的语法分析工具,它最显著的优点就是扩展性,修改语法规则只需要修改相应的非终结符就可以了,若扩展语法,只需要增加非终结符类就可以了。

缺点

解释器模式会引起类的膨胀,每个语法都需要产生一个非终结符表达式,语法规则比较复杂时,就可能产生大量的类文件,为维护带来非常多的麻烦。同时,由于采用递归调用方法,每个非终结符表达式只关心与自己相关的表达式,每个表达式需要知道最终的结果,必须通过递归方式,无论是面向对象的语言还是面向过程的语言,递归都是一个不推荐的方式。由于使用了大量的循环和递归,效率是一个不容忽视的问题。特别是用于解释一个解析复杂、冗长的语法时,效率是难以忍受的。

适用场景

在以下情况下可以使用解释器模式:

  • 有一个简单的语法规则,比如一个sql语句,如果我们需要根据sql语句进行rm转换,就可以使用解释器模式来对语句进行解释。
  • 一些重复发生的问题,比如加减乘除四则运算,但是公式每次都不同,有时是a+b-cd,有时是ab+c-d,等等等等个,公式千变万化,但是都是由加减乘除四个非终结符来连接的,这时我们就可以使用解释器模式。

注意事项

解释器模式真的是一个比较少用的模式,因为对它的维护实在是太麻烦了,想象一下,一坨一坨的非终结符解释器,假如不是事先对文法的规则了如指掌,或者是文法特别简单,则很难读懂它的逻辑。解释器模式在实际的系统开发中使用的很少,因为他会引起效率、性能以及维护等问题。

评论和共享

定义

提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部细节。

类型

行为类模式

类图

Iterator Pattern

迭代器模式的结构

  • 抽象容器:一般是一个接口,提供一个iterator()方法,例如java中的Collection接口,List接口,Set接口等。
  • 具体容器:就是抽象容器的具体实现类,比如List接口的有序列表实现ArrayList,List接口的链表实现LinkList,Set接口的哈希列表的实现HashSet等。
  • 抽象迭代器:定义遍历元素所需要的方法,一般来说会有这么三个方法:取得第一个元素的方法first(),取得下一个元素的方法next(),判断是否遍历结束的方法isDone()(或者叫hasNext()),移出当前对象的方法remove(),
  • 迭代器实现:实现迭代器接口中定义的方法,完成集合的迭代。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
interface Iterator {  
public Object next();
public boolean hasNext();
}
class ConcreteIterator implements Iterator{
private List list = new ArrayList();
private int cursor =0;
public ConcreteIterator(List list){
this.list = list;
}
public boolean hasNext() {
if(cursor==list.size()){
return false;
}
return true;
}
public Object next() {
Object obj = null;
if(this.hasNext()){
obj = this.list.get(cursor++);
}
return obj;
}
}
interface Aggregate {
public void add(Object obj);
public void remove(Object obj);
public Iterator iterator();
}
class ConcreteAggregate implements Aggregate {
private List list = new ArrayList();
public void add(Object obj) {
list.add(obj);
}

public Iterator iterator() {
return new ConcreteIterator(list);
}

public void remove(Object obj) {
list.remove(obj);
}
}
public class Client {
public static void main(String[] args){
Aggregate ag = new ConcreteAggregate();
ag.add("小明");
ag.add("小红");
ag.add("小刚");
Iterator it = ag.iterator();
while(it.hasNext()){
String str = (String)it.next();
System.out.println(str);
}
}
}

优点

  • 简化了遍历方式,对于对象集合的遍历,还是比较麻烦的,对于数组或者有序列表,我们尚可以通过游标来取得,但用户需要在对集合了解很清楚的前提下,自行遍历对象,但是对于hash表来说,用户遍历起来就比较麻烦了。而引入了迭代器方法后,用户用起来就简单的多了。
  • 可以提供多种遍历方式,比如说对有序列表,我们可以根据需要提供正序遍历,倒序遍历两种迭代器,用户用起来只需要得到我们实现好的迭代器,就可以方便的对集合进行遍历了。
  • 封装性良好,用户只需要得到迭代器就可以遍历,而对于遍历算法则不用去关心。

缺点

对于比较简单的遍历(像数组或者有序列表),使用迭代器方式遍历较为繁琐,大家可能都有感觉,像ArrayList,我们宁可愿意使用for循环和get方法来遍历集合。

适用场景

  • 迭代器模式是与集合共生共死的,一般来说,我们只要实现一个集合,就需要同时提供这个集合的迭代器,就像java中的Collection,List、Set、Map等,这些集合都有自己的迭代器。假如我们要实现一个这样的新的容器,当然也需要引入迭代器模式,给我们的容器实现一个迭代器。
  • 但是,由于容器与迭代器的关系太密切了,所以大多数语言在实现容器的时候都给提供了迭代器,并且这些语言提供的容器和迭代器在绝大多数情况下就可以满足我们的需要,所以现在需要我们自己去实践迭代器模式的场景还是比较少见的,我们只需要使用语言中已有的容器和迭代器就可以了。

评论和共享

作者的图片

Archie Shi

Nothing to say


Front-End Development Engineer