定义:责任链模式(Chain of Responsibility),使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些关系对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
结构图:
所涉及的角色:
抽象处理者(Handler)角色:定义出一个处理请求的接口。如果需要,接口可以定义 出一个方法以设定和返回对下家的引用。这个角色通常由一个Java抽象类或者Java接口实现。上图中Handler类的聚合关系给出了具体子类对下家的引用,抽象方法handleRequest()规范了子类处理请求的操作。
具体处理者(ConcreteHandler)角色:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家。
代码示例:
全局变量,接口类型 Levels
package com.jin.model.chain; // 全局变量,接口类型 /** * 使用Java中的interface定义全局变量,可根据具体需要在 具体的包中使用静态导入相关的全局变量,语法如下: import static * package01.package02.*; */ public interface Levels { public static final int LEVEL_01 = 1; public static final int LEVEL_02 = 2; public static final int LEVEL_03 = 3; }
抽象请求类:AbstractRequest
package com.jin.model.chain; // 抽象请求类 public abstract class AbstractRequest { private String content; public AbstractRequest(String content) { this.content = content; } public String getContent() { return this.content; } // 获得请求的级别 public abstract int getRequestLevel(); }
具体请求类01:Request01
package com.jin.model.chain; // 具体请求类01 public class Request01 extends AbstractRequest { public Request01(String content) { super(content); } @Override public int getRequestLevel() { return Levels.LEVEL_01; } }
具体请求类02:Request02
package com.jin.model.chain; // 具体请求类02 public class Request02 extends AbstractRequest { public Request02(String content) { super(content); } @Override public int getRequestLevel() { return Levels.LEVEL_02; } }
具体请求类03:Request03
package com.jin.model.chain; // 具体请求类03 public class Request03 extends AbstractRequest { public Request03(String content) { super(content); } @Override public int getRequestLevel() { return Levels.LEVEL_03; } }
抽象处理者类:AbstractHandler
package com.jin.model.chain; // 抽象处理者类, public abstract class AbstractHandler { // 责任链的下一个节点,即处理者 private AbstractHandler nextHandler = null; // 捕获具体请求并进行处理,或是将请求传递到责任链的下一级别 public final void handleRequest(AbstractRequest request) { // 若该请求与当前处理者的级别层次相对应,则由自己进行处理 if (this.getHandlerLevel() == request.getRequestLevel()) { this.handle(request); } else { // 当前处理者不能胜任,则传递至职责链的下一节点 if (this.nextHandler != null) { System.out.println("当前 处理者-0" + this.getHandlerLevel() + " 不足以处理 请求-0" + request.getRequestLevel()); // 这里使用了递归调用 this.nextHandler.handleRequest(request); } else { System.out.println("职责链上的所有处理者都不能胜任该请求..."); } } } // 设置责任链中的下一个处理者 public void setNextHandler(AbstractHandler nextHandler) { this.nextHandler = nextHandler; } // 获取当前处理者的级别 protected abstract int getHandlerLevel(); // 定义链中每个处理者具体的处理方式 protected abstract void handle(AbstractRequest request); }
具体处理者-01:Handler01
package com.jin.model.chain; // 具体处理者-01 public class Handler01 extends AbstractHandler { @Override protected int getHandlerLevel() { return Levels.LEVEL_01; } @Override protected void handle(AbstractRequest request) { System.out.println("处理者-01 处理 " + request.getContent() + "\n"); } }
具体处理者-02:Handler02
package com.jin.model.chain; // 具体处理者-02 public class Handler02 extends AbstractHandler { @Override protected int getHandlerLevel() { return Levels.LEVEL_02; } @Override protected void handle(AbstractRequest request) { System.out.println("处理者-02 处理 " + request.getContent() + "\n"); } }
具体处理者-03:Handler03
package com.jin.model.chain; // 具体处理者-03 public class Handler03 extends AbstractHandler { @Override protected int getHandlerLevel() { return Levels.LEVEL_03; } @Override protected void handle(AbstractRequest request) { System.out.println("处理者-03 处理 " + request.getContent() + "\n"); } }
客户端:Client
package com.jin.model.chain; // 测试类 public class Client { public static void main(String[] args) { // 创建指责链的所有节点 AbstractHandler handler01 = new Handler01(); AbstractHandler handler02 = new Handler02(); AbstractHandler handler03 = new Handler03(); // 进行链的组装,即头尾相连,一层套一层 handler01.setNextHandler(handler02); handler02.setNextHandler(handler03); // 创建请求并提交到指责链中进行处理 AbstractRequest request01 = new Request01("请求-01"); AbstractRequest request02 = new Request02("请求-02"); AbstractRequest request03 = new Request03("请求-03"); // 每次提交都是从链头开始遍历 handler01.handleRequest(request01); handler01.handleRequest(request02); handler01.handleRequest(request03); } }运行结果:
好处:
- 接受者和发送者都没有对方的明确信息,且链中的对象自己也不知道链的结构,结果是责任链可简化对象的相互连接,它们仅需保持一个指向其后继者的引用,而不需保持它所有的候选者的引用,大大降低了耦合度。
- 由客户端来定义链的结构,可以随时随地增加或修改处理一个请求的结构,增加了给对象指派职责的灵活性。
- 发出请求的客户端并不知道哪一个对象最终处理这个请求,这样系统的更改可以在不影响客户端的情况下动态地重新组织和分配责任。
不足之处:
- 对于每一个请求都需要遍历职责链,性能是个问题;
- 抽象处理者 AbstractHandler 类中的 handleRequest() 方法中使用了递归,栈空间的大小也是个问题。
首先:从总体上为三层架构的理论知识简单的画了一张思维导图。
其次:在网上搜寻了一个关于三层的例子,进行了一下实战的练习。具体代码不作展示,将其中较难理解的部分稍微解释。
第一部分:
cmd.Parameters.Add(newSqlParameter("@UserName",userName)); cmd.Parameters.Add(newSqlParameter("@Password",password));
解释:
①SqlParameter是sql的参数类,在这里实例化了一个这样的类,并传入参数userName,现在需要把这个参数加入到Command的参数列中,之后执行的时候才会用userName替换掉sql语句中的@userName。
②以上这种方式为匿名方式,因为cmd.Parameters.Add方法需要传入一个SqlParameter的实例
③第二种方式:
SqlParameterpar = new SqlParameter("@UserName",userName); cmd.Parameters.Add(par);
④SqlParameter的构造函数,第一个参数是在sql语句中的占位符,第二个参数是需要传入的参数,@是一个占位符。
第二部分:
user.ID =reader.GetInt32(0); user.UserName =reader.GetString(1); user.Password =reader.GetString(2);
解释:
①获取指定列的 32位有符号整数形式的值。GetInt32(0)=输入的 32位有符号整数形式的值。显示来是用户的ID号也就是一串数字。后两句相信你根据第一句的解释你也就会了。
②第二种方式:
user.ID =int.Parse(reader["ID"].ToString ()); user.UserName =reader["UserName"].ToString(); user.PassWord =reader["Password"].ToString();
第三部分:
三层的调用关系,我用一张包图向大家展示它们之间的调用关系。
最后:展示一下我模拟三层的例子自己为机房收费系统画的包图、类图以及时序图,由于包图已在上面进行了展示,就不再赘述。
1.类图:
UI层:
BLL层:
DAL层:
实体层:
2.时序图:
以上就是我对三层架构的全部总结。希望读者批评指正。
在C语言的应用领域,如通讯领域和嵌入式系统领域,一个的软件项目通常包含很多复杂的功能,实现这个项目不是一个程序员单枪匹马可以胜任的,往往需要一个团队的有效合作,另外,在一个以C代码为主的完整的项目中,经常也需要加入一些其他语言的代码,例如,C代码和汇编代码的混合使用,C文件和C++的同时使用。这些都增加了一个软件项目的复杂程度,为了提高软件质量,合理组织的各种代码和文件是非常重要的。
组织代码和文件的目的是为了使团队合作更加有效,使软件项目有良好的可扩展性、可维护性、可移植性、可裁减、可测试性,防止错误发生,提高软件的稳定性。通常情况下,软件项目采用层次化结构和模块化开发的方法,例如,一个嵌入式软件项目可能有驱动层,操作系统层,功能层,应用程序层,每一个层使用它的下层提供的接口,并为它的上层提供调用接口,模块则是每一个层中完成一个功能的单元,例如驱动层的每一个设备的驱动就是一个模块,应用层的每个应用程序就是一个模块,模块使用下层提供的接口和同层其他模块提供的接口,完成特定功能,为上层和同层的其他模块提供调用接口。
这里的接口是指一个功能模块暴露出来的,提供给其他模块的访问具体功能的方法。根据C语言的特点,使用*.c文件实现模块的功能,使用*.h文件暴露单元的接口,在*.h文件里声明外部其他模块可能是用的函数,数据类型,全局变量,类型定义,宏定义和常量定义.外部模块只需包含*.h文件就可以使用相应的功能.当然,模块可以在细化为子模块.虽然我们这里说的接口和COM(通用组件模型)里定义的接口不同,但是,根据COM里对接口的讨论,为了使软件在修改时,一个模块的修改不会影响到其他模块的一个模块的修改不会导致其他模块也需要修改,所以,接口第一次发布后,修改*.h文件不能导致使用这个接口的其他模块需要重新编写.
根据C语言的特点,并借鉴一些成熟软件项目代码,总结C项目中代码文件组织的基本建议:
1,使用层次化和模块化的软件开发模型.每一个模块只能使用所在层和下一层模块提供的接口.
2,每个模块的文件包存在独立的一个文件夹中.通常情况下,实现一个模块的文件不止一个,这些相关的文件应该保存在一个文件夹中.
3,用于模块裁减的条件编译宏保存在一个独立的文件里,便于软件裁减.
4,硬件相关代码和操作系统相关代码与纯C代码相对独立保存,以便于软件移植.
5,声明和定义分开,使用*.h文件暴露模块需要提供给外部的函数,宏,类型,常量,全局变量,尽量做到模块对外部透明,用户在使用模块功能时不需要了解具体的实现,文件一旦发布,要修改一定要很慎重,
6,文件夹和文件命名要能够反映出模块的功能.
7,正式版本和测试版本使用统一文件,使用宏控制是否产生测试输出。
8,必要的注释不可缺少。
理想的情况下,一个可执行的模块提供一个公开的接口,即使用一个*.h文件暴露接口,但是,有时候,一个模块需要提供不止一个接口,这时,就要为每个定义的接口提供一个公开的接口。在C语言的里,每个C文件是一个模块,头文件为使用这个模块的用户提供接口,用户只要包含相应的头文件就可以使用在这个头文件中暴露的接口。所有的头文件都建议参考以下的规则:
1, 头文件中不能有可执行代码,也不能有数据的定义,只能有宏、类型(typedef,struct,union,menu),数据和函数的声明。例如以下的代码可以包含在头文件里:
#define NAMESTRING “name”
typedef unsign long word;
menu{
flag1;
flag2;
};
typedef struct{
int x;
int y;
} Piont;
extent Fun(void);
extent int a;
全局变量和函数的定义不能出现在*.h文件里。例如下面的代码不能包含在头文件:
int a;
void Fun1(void)
{
a++;
}
2,头文件中不能包本地数据(模块自己使用的数据或函数,不被其他模块使用)。这一点相当于面向对象程序设计里的私有成员,即只有模块自己使用的函数,数据,不要用extent在头文件里声明,只有模块自己使用的宏,常量,类型也不要在头文件里声明,应该在自己的*.c文件里声明。
3,含一些需要使用的声明。在头文件里声明外部需要使用的数据,函数,宏,类型。
4,防止被重复包含。使用下面的宏防止一个头文件被重复包含。
#ifndef MY_INCLUDE_H
#define MY_INCLUDE_H
<头文件内容 >
#endif
5,包含extern "C",使的程序可以在C++编译器被编译
#ifdef __cplusplus
extern "C"{
#endif
<函数声明 >
#ifdef __cplusplus
}
#enfif
被extern "C"修饰的变量和函数是按照C语言方式编译和连接的;未加extern “C”声明时的编译方式,作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为:
void foo(