设计模式(1)-行为类

参考文章:

史上最全设计模式导学目录(完整版): http://blog.csdn.net/lovelion/article/details/17517213

一、六大原则简述

  • 单一职责:不要存在多于一个导致类变更的原因。**通俗的说,即一个类只负责一项职责。
  • 里式替换:所有引用基类的地方必须能透明地使用其子类的对象。重载须慎重,能不重载就不重载。
  • 依赖倒置:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
  • 接口隔离:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
  • 迪米特法则:最少知道法则。只与直接的朋友通信。首先来解释一下什么是直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖、关联、组合、聚合等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的形式出现在类的内部。
  • 开闭法则:对修改封闭,对扩展开放。

二、责任链模式

职责链模式(Chain of Responsibility Pattern):避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。职责链模式是一种对象行为型模式。
职责链模式结构的核心在于引入了一个抽象处理者。职责链模式结构如图所示:

职责链模式的核心在于抽象处理者类的设计,抽象处理者的典型代码如下所示:

abstract class Handler {
    //维持对下家的引用
protected Handler successor;

    public void setSuccessor(Handler successor) {
        this.successor=successor;
    }

    public abstract void handleRequest(String request);
}

上述代码中,抽象处理者类定义了对下家的引用对象,以便将请求转发给下家,该对象的访问符可设为 protected,在其子类中可以使用。在抽象处理者类中声明了抽象的请求处理方法,具体实现交由子类完成。

具体处理者是抽象处理者的子类,它具有两大作用:第一是处理请求,不同的具体处理者以不同的形式实现抽象请求处理方法 handleRequest();第二是转发请求,如果该请求超出了当前处理者类的权限,可以将该请求转发给下家。具体处理者类的典型代码如下:

class ConcreteHandler extends Handler {
    public void handleRequest(String request) {
        if (请求满足条件) {
            //处理请求
        }
        else {
            this.successor.handleRequest(request);  //转发请求
        }
    }
}

举个例子:jdk中的ClassLoader设计。

职责链模式总结

职责链模式通过建立一条链来组织请求的处理者,请求将沿着链进行传递,请求发送者无须知道请求在何时、何处以及如何被处理,实现了请求发送者与处理者的解耦。在软件开发中,如果遇到有多个对象可以处理同一请求时可以应用职责链模式,例如在 Web 应用开发中创建一个过滤器(Filter)链来对请求数据进行过滤,在工作流系统中实现公文的分级审批等等,使用职责链模式可以较好地解决此类问题。

主要优点

职责链模式的主要优点如下:

(1) 职责链模式使得一个对象无须知道是其他哪一个对象处理其请求,对象仅需知道该请求会被处理即可,接收者和发送者都没有对方的明确信息,且链中的对象不需要知道链的结构,由客户端负责链的创建,降低了系统的耦合度。

(2) 请求处理对象仅需维持一个指向其后继者的引用,而不需要维持它对所有的候选处理者的引用,可简化对象的相互连接。

(3) 在给对象分派职责时,职责链可以给我们更多的灵活性,可以通过在运行时对该链进行动态的增加或修改来增加或改变处理一个请求的职责。

(4) 在系统中增加一个新的具体请求处理者时无须修改原有系统的代码,只需要在客户端重新建链即可,从这一点来看是符合“开闭原则”的。

主要缺点

职责链模式的主要缺点如下:

(1) 由于一个请求没有明确的接收者,那么就不能保证它一定会被处理,该请求可能一直到链的末端都得不到处理;一个请求也可能因职责链没有被正确配置而得不到处理。

(2) 对于比较长的职责链,请求的处理可能涉及到多个处理对象,系统性能将受到一定影响,而且在进行代码调试时不太方便。

(3) 如果建链不当,可能会造成循环调用,将导致系统陷入死循环。

适用场景

在以下情况下可以考虑使用职责链模式:

(1) 有多个对象可以处理同一个请求,具体哪个对象处理该请求待运行时刻再确定,客户端只需将请求提交到链上,而无须关心请求的处理对象是谁以及它是如何处理的。

(2) 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。

(3) 可动态指定一组对象处理请求,客户端可以动态创建职责链来处理请求,还可以改变链中处理者之间的先后次序。

三、命令模式

命令模式(Command Pattern):将一个请求封装为一个对象,从而让我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。命令模式是一种对象行为型模式,其别名为动作(Action)模式或事务(Transaction)模式。

命令模式的定义比较复杂,提到了很多术语,例如“用不同的请求对客户进行参数化”、“对请求排队”、“记录请求日志”、“支持可撤销操作”等,在后面我们将对这些术语进行一一讲解。

命令模式的核心在于引入了命令类,通过命令类来降低发送者和接收者的耦合度,请求发送者只需指定一个命令对象,再通过命令对象来调用请求接收者的处理方法,其结构如图所示:

在命令模式结构图中包含如下几个角色:

  • Command(抽象命令类):抽象命令类一般是一个抽象类或接口,在其中声明了用于执行请求的 execute() 等方法,通过这些方法可以调用请求接收者的相关操作。
  • ConcreteCommand(具体命令类):具体命令类是抽象命令类的子类,实现了在抽象命令类中声明的方法,它对应具体的接收者对象,将接收者对象的动作绑定其中。在实现 execute() 方法时,将调用接收者对象的相关操作(Action)。
  • Invoker(调用者):调用者即请求发送者,它通过命令对象来执行请求。一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令类之间存在关联关系。在程序运行时可以将一个具体命令对象注入其中,再调用具体命令对象的 execute() 方法,从而实现间接调用请求接收者的相关操作。
  • Receiver(接收者):接收者执行与请求相关的操作,它具体实现对请求的业务处理。

以jdk中的线程类设计为例:

主要优点

命令模式的主要优点如下:

(1) 降低系统的耦合度。由于请求者与接收者之间不存在直接引用,因此请求者与接收者之间实现完全解耦,相同的请求者可以对应不同的接收者,同样,相同的接收者也可以供不同的请求者使用,两者之间具有良好的独立性。

(2) 新的命令可以很容易地加入到系统中。由于增加新的具体命令类不会影响到其他类,因此增加新的具体命令类很容易,无须修改原有系统源代码,甚至客户类代码,满足“开闭原则”的要求。

(3) 可以比较容易地设计一个命令队列或宏命令(组合命令)。

(4) 为请求的撤销(Undo)和恢复(Redo)操作提供了一种设计和实现方案。

主要缺点

命令模式的主要缺点如下:

使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个对请求接收者的调用操作都需要设计一个具体命令类,因此在某些系统中可能需要提供大量的具体命令类,这将影响命令模式的使用。

适用场景

在以下情况下可以考虑使用命令模式:

(1) 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。请求调用者无须知道接收者的存在,也无须知道接收者是谁,接收者也无须关心何时被调用。

(2) 系统需要在不同的时间指定请求、将请求排队和执行请求。一个命令对象和请求的初始调用者可以有不同的生命期,换言之,最初的请求发出者可能已经不在了,而命令对象本身仍然是活动的,可以通过该命令对象去调用请求接收者,而无须关心请求调用者的存在性,可以通过请求日志文件等机制来具体实现。

(3) 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。

(4) 系统需要将一组操作组合在一起形成宏命令。

四、观察者模式

观察者模式被用的非常的多...

观察者模式(Observer Pattern):定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式的别名包括发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式是一种对象行为型模式。

下面举个例子,为了实现对象之间的联动,Sunny 软件公司开发人员决定使用观察者模式来进行多人联机对战游戏的设计,其基本结构如图所示:

在图中,AllyControlCenter 充当目标类,ConcreteAllyControlCenter 充当具体目标类,Observer 充当抽象观察者,Player 充当具体观察者。完整代码如下所示:

import java.util.*;

//抽象观察类
interface Observer {
    public String getName();
    public void setName(String name);
    public void help(); //声明支援盟友方法
    public void beAttacked(AllyControlCenter acc); //声明遭受攻击方法
}

//战队成员类:具体观察者类
class Player implements Observer {
    private String name;

    public Player(String name) {
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

    //支援盟友方法的实现
    public void help() {
        System.out.println("坚持住," + this.name + "来救你!");
    }

    //遭受攻击方法的实现,当遭受攻击时将调用战队控制中心类的通知方法notifyObserver()来通知盟友
    public void beAttacked(AllyControlCenter acc) {
        System.out.println(this.name + "被攻击!");
        acc.notifyObserver(name);
    }
}

//战队控制中心类:目标类
abstract class AllyControlCenter {
    protected String allyName; //战队名称
    protected ArrayList<Observer> players = new ArrayList<Observer>(); //定义一个集合用于存储战队成员

    public void setAllyName(String allyName) {
        this.allyName = allyName;
    }

    public String getAllyName() {
        return this.allyName;
    }

    //注册方法
    public void join(Observer obs) {
        System.out.println(obs.getName() + "加入" + this.allyName + "战队!");
        players.add(obs);
    }

    //注销方法
    public void quit(Observer obs) {
        System.out.println(obs.getName() + "退出" + this.allyName + "战队!");
        players.remove(obs);
    }

    //声明抽象通知方法
    public abstract void notifyObserver(String name);
}

//具体战队控制中心类:具体目标类
class ConcreteAllyControlCenter extends AllyControlCenter {
    public ConcreteAllyControlCenter(String allyName) {
        System.out.println(allyName + "战队组建成功!");
        System.out.println("----------------------------");
        this.allyName = allyName;
    }

    //实现通知方法
    public void notifyObserver(String name) {
        System.out.println(this.allyName + "战队紧急通知,盟友" + name + "遭受敌人攻击!");
        //遍历观察者集合,调用每一个盟友(自己除外)的支援方法
        for(Object obs : players) {
            if (!((Observer)obs).getName().equalsIgnoreCase(name)) {
                ((Observer)obs).help();
            }
        }
    }
}

编写如下客户端测试代码:

class Client {
    public static void main(String args[]) {
        //定义观察目标对象
AllyControlCenter acc;
        acc = new ConcreteAllyControlCenter("金庸群侠");

        //定义四个观察者对象
        Observer player1,player2,player3,player4;

        player1 = new Player("杨过");
        acc.join(player1);

        player2 = new Player("令狐冲");
        acc.join(player2);

        player3 = new Player("张无忌");
        acc.join(player3);

        player4 = new Player("段誉");
        acc.join(player4);

        //某成员遭受攻击
        Player1.beAttacked(acc);
    }
}

编译并运行程序,输出结果如下:

金庸群侠战队组建成功!
----------------------------
杨过加入金庸群侠战队!
令狐冲加入金庸群侠战队!
张无忌加入金庸群侠战队!
段誉加入金庸群侠战队!
杨过被攻击!
金庸群侠战队紧急通知,盟友杨过遭受敌人攻击!
坚持住,令狐冲来救你!
坚持住,张无忌来救你!
坚持住,段誉来救你!

JDK中的观察者模式:

五、模板方法模式

模板定义一个操作中的算法的骨架,而将步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义算法的某些特定步骤。

模板方法不会涉及太多的类,抽象类和其具体实现类。

基本方法

基本方法是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种:抽象方法(Abstract Method)、具体方法(Concrete Method)和钩子方法(Hook Method)。

(1) 抽象方法:一个抽象方法由抽象类声明、由其具体子类实现。在 C# 语言里一个抽象方法以 abstract 关键字标识。

(2) 具体方法:一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承。

(3) 钩子方法:一个钩子方法由一个抽象类或具体类声明并实现,而其子类可能会加以扩展。通常在父类中给出的实现是一个空实现(可使用 virtual 关键字将其定义为虚函数),并以该空实现作为方法的默认实现,当然钩子方法也可以提供一个非空的默认实现。

在模板方法模式中,钩子方法有两类:第一类钩子方法可以与一些具体步骤“挂钩”,以实现在不同条件下执行模板方法中的不同步骤,这类钩子方法的返回类型通常是 bool 类型的,这类方法名一般为 IsXXX(),用于对某个条件进行判断,如果条件满足则执行某一步骤,否则将不执行,如下代码片段所示:

……
//模板方法
public void TemplateMethod()
{
Open();
Display();
//通过钩子方法来确定某步骤是否执行
if (IsPrint())
{
    Print();
}
}

//钩子方法
public bool IsPrint()
{
    return true;
}
……

在代码中 IsPrint() 方法即是钩子方法,它可以决定 Print() 方法是否执行,一般情况下,钩子方法的返回值为 true,如果不希望某方法执行,可以在其子类中覆盖钩子方法,将其返回值改为 false 即可,这种类型的钩子方法可以控制方法的执行,对一个算法进行约束。

还有一类钩子方法就是实现体为空的具体方法,子类可以根据需要覆盖或者继承这些钩子方法,与抽象方法相比,这类钩子方法的好处在于子类如果没有覆盖父类中定义的钩子方法,编译可以正常通过,但是如果没有覆盖父类中声明的抽象方法,编译将报错。

在模板方法模式中,抽象类的典型代码如下:

abstract class AbstractClass
{
//模板方法
public void TemplateMethod()
{
        PrimitiveOperation1();
        PrimitiveOperation2();
        PrimitiveOperation3();
}

//基本方法—具体方法
public void PrimitiveOperation1()
{
    //实现代码
}

//基本方法—抽象方法
    public abstract void PrimitiveOperation2();    

//基本方法—钩子方法
public virtual void PrimitiveOperation3()
{  }
}

在抽象类中,模板方法 TemplateMethod() 定义了算法的框架,在模板方法中调用基本方法以实现完整的算法,每一个基本方法如 PrimitiveOperation1()、PrimitiveOperation2() 等均实现了算法的一部分,对于所有子类都相同的基本方法可在父类提供具体实现,例如 PrimitiveOperation1(),否则在父类声明为抽象方法或钩子方法,由不同的子类提供不同的实现,例如 PrimitiveOperation2() 和 PrimitiveOperation3()。

可在抽象类的子类中提供抽象步骤的实现,也可覆盖父类中已经实现的具体方法,具体子类的典型代码如下:

class ConcreteClass : AbstractClass
{
public override void PrimitiveOperation2()
{
    //实现代码
}

public override void PrimitiveOperation3()
{
    //实现代码
}
}

在模板方法模式中,由于面向对象的多态性,子类对象在运行时将覆盖父类对象,子类中定义的方法也将覆盖父类中定义的方法,因此程序在运行时,具体子类的基本方法将覆盖父类中定义的基本方法,子类的钩子方法也将覆盖父类的钩子方法,从而可以通过在子类中实现的钩子方法对父类方法的执行进行约束,实现子类对父类行为的反向控制。

jdk中的模板方法也非常的多

六、策略模式

在策略模式中,我们可以定义一些独立的类来封装不同的算法,每一个类封装一种具体的算法,在这里,每一个封装算法的类我们都可以称之为一种策略(Strategy),为了保证这些策略在使用时具有一致性,一般会提供一个抽象的策略类来做规则的定义,而每种算法则对应于一个具体策略类。

在策略模式结构图中包含如下几个角色:

  • Context(环境类):环境类是使用算法的角色,它在解决某个问题(即实现某个方法)时可以采用多种策略。在环境类中维持一个对抽象策略类的引用实例,用于定义所采用的策略。
  • Strategy(抽象策略类):它为所支持的算法声明了抽象方法,是所有策略类的父类,它可以是抽象类或具体类,也可以是接口。环境类通过抽象策略类中声明的方法在运行时调用具体策略类中实现的算法。
  • ConcreteStrategy(具体策略类):它实现了在抽象策略类中声明的算法,在运行时,具体策略类将覆盖在环境类中定义的抽象策略类对象,使用一种具体的算法实现某个业务处理。

举个例子,jdk线程池有界队列的拒绝策略就是典型的策略模式

优点:

1、策略类之间可以自由切换,由于策略类实现自同一个抽象,所以他们之间可以自由切换。

2、易于扩展,增加一个新的策略对策略模式来说非常容易,基本上可以在不改变原有代码的基础上进行扩展。

3、避免使用多重条件,如果不使用策略模式,对于所有的算法,必须使用条件语句进行连接,通过条件判断来决定使用哪一种算法,在上一篇文章中我们已经提到,使用多重条件判断是非常不容易维护的。

缺点:

1、维护各个策略类会给开发带来额外开销,可能大家在这方面都有经验:一般来说,策略类的数量超过5个,就比较令人头疼了。

2、必须对客户端(调用者)暴露所有的策略类,因为使用哪种策略是由客户端来决定的,因此,客户端应该知道有什么策略,并且了解各种策略之间的区别,否则,后果很严重。

适用场景

1、多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。

2、需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。

3、 对客户隐藏具体策略(算法)的实现细节,彼此完全独立。

时间: 2024-10-09 16:29:41

设计模式(1)-行为类的相关文章

设计模式之UML类图的常见关系

设计模式之UML类图的常见关系 本文来自转载 烧点饭博客 本篇会讲解在UML类图中,常见几种关系: 泛化(Generalization),依赖(Dependency),关联(Association),聚合(Aggregation),组合(Composition). 1.泛化关系 泛化关系是继承或实现的关系,是is a关系,具体表现为类与类的继承,接口与接口的继承,类对接口的实现关系. 2.依赖关系 依赖关系表示为一个类使用另一个类,这种使用关系是具有偶然性的.临时性的.非常弱的,一个类的变化会影

设计模式前言——UML类图

设计模式前言--UML类图 一.UML类图 1.类 类(Class)封装了数据和行为,是面向对象的重要组成部分,是具有相同属性.操作.关系的对象集合的总称.在系统中,每个类都具有一定的职责,职责指的是类要完成什么样的功能,要承担什么样的义务.一个类可以有多种职责,设计得好的类一般只有一种职责.在定义类的时候,将类的职责分解成为类的属性和操作(即方法).类的属性即类的数据职责,类的操作即类的行为职责.设计类是面向对象设计中最重要的组成部分,也是最复杂和最耗时的部分.在软件系统运行时,类将被实例化成

设计模式之行为类模式大PK

                                    行为类模式大PK 行为类模式包括责任链模式.命令模式.解释器模式.迭代器模式.中介者模式.备忘录模式.观察者模式.状态模式.策略模式.模板方法模式.访问者模式.该组设计模式众多,如下我们着重介绍一下命令模式VS策略模式.状态模式VS策略模式.观察者模式VS责任链模式. 命令模式VS策略模式 命令模式和策略模式类图很相似,只是命令模式多了一个接收者(Receiver)角色,通过确切的Command类调用Receiver类,实现

浅谈设计模式之工厂类模式由简单到复杂的演变

前言 在软件设计过程中,我们总是需要创建很多对象,而且系统越庞大,创建的对象越复杂.而今天我们将讨论的就是解决对象创建时的难题--工厂类模式.为了贴近工厂这个词,我们采用工厂建造汽车这个例子来阐明工厂类模式的演变和什么场景下使用什么模式. 场景1.:一位顾客要开车从上海到苏州,他需要一辆汽车,于是他自己组装汽车,给车装轮胎.导航仪.车灯等. 问题:1.显然,顾客只是想拥有一辆汽车,他不想知道怎么去买汽车,更不想知道怎么组装,然后还要给汽车上漆. 2.如果他想换个型号的汽车,他得重新来遍组装汽车.

iOS设计模式 - (2)UML类间关系精解

在正式讲设计模式之前, 介绍一下UML类图之间的关系还是非常有必要的, 由于一些教程, 书籍, 包含我之后的文章, 都会大量使用类图, 去描写叙述各个类之间的关系.这是一种非常直观, 简约的方式. 当然, 能力, 精力有限, 这里的UML的介绍也仅仅局限与几种常见的类间关系. 包含: 继承.实现.依赖.关联.聚合.组合 在次之前, 假设看不懂类图, 能够先看一下我之前写的一篇文章 : 具体解释八大UML类图符号的表示法 iOS - UML类间关系精解           by Colin丶 转载

设计模式之UML类图

本文是自己学习所做笔记,欢迎转载,但请注明出处:http://blog.csdn.net/jesson20121020 之前,在工厂方法模式中画的图,不是很严格的符合UML类图,所以今天特地看了一下UML类图,下面内容大部分摘自"大话设计模式". UML的基本图示法 UML类图图示样例 类图: 如上图,矩形框就代表一个类(Class).分三层,第一层显示类的名称,如果是抽象类,则就用斜体显示.第二层是为类的特性,通常就是字段和属性.第三层是类的操作,通常是方法或行为.注意前面的的符号,

对Java单例设计模式中懒汉式类定义的讨论

全世界人民都知道单例设计模式中类的定义分为懒汉式和饿汉式两种,然而今天并不是要把它们做横向比较.实际上,不论饿汉式类的代码看起来有多么美轮美奂,在实际开发中它的效率总是不如懒汉式的.然而在笔试和面试中懒汉式的出镜率可以说是比饿汉式不知道高到哪里去了,因此把它完全弄懂应该是十分有必要的. 饿汉式: class Single1 { int num = 1; private static Single1 single1 = new Single1(); private Single1(){} stat

DAO设计模式 -- 使用数据库连接类连接MySql数据库并实现添加用户

1. DAO简介    DAO设计模式是属于J2EE数据库层的操作,使用DAO设计模式可以简化大量代码,增强程序的可移植性. 2. DAO各部分详解    DAO设计模式包括5个重要的部分,分别为数据库连接类,VO类,DAO接口,DAO实现类以及DAO工厂类.   2-1 数据库连接类:    数据库连接类的主要功能就是连接数据库并获得连接对象,以及关闭数据库.通过数据库连接类可以大大地简化开发,在需要进行数据库连接时,只需要创建该类的实例,并调用其中的方法就可以获得数据库连接对象和关闭数据库而

python编程设计模式之接口类和抽象类

接口类 """ 接口类 是一种编程设计模式,在python原本没有接口类 借用Java思想创建的一种规范设计模式 支持多继承,进行多方面规范 """ 例子: from abc import abstractmethod, ABCMeta class Clergy(metaclass=ABCMeta): # 规范子类都要有cure方法 @abstractmethod # 装饰要规范的方法 def cure(self, HP): # 规范方法必须要p