java

位置:IT落伍者 >> java >> 浏览文章

设计模式分解java(6)


发布日期:2019年01月26日
 
设计模式分解java(6)

/***/

行为模式Command

Command模式是最让我疑惑的一个模式我在阅读了很多代码后

才感觉隐约掌握其大概原理我认为理解设计模式最主要是掌握起原理构造

这样才对自己实际编程有指导作用Command模式实际上不是个很具体

规定很多的模式正是这个灵活性让人有些confuse

Command定义

不少Command模式的代码都是针对图形界面的它实际就是菜单命令

我们在一个下拉菜单选择一个命令时然后会执行一些动作

将这些命令封装成在一个类中然后用户(调用者)再对这个类进行操作这就是Command模式

换句话说本来用户(调用者)是直接调用这些命令的如菜单上打开文档(调用者)

就直接指向打开文档的代码使用Command模式就是在这两者之间增加一个中间者

将这种直接关系拗断同时两者之间都隔离基本没有关系了

显然这样做的好处是符合封装的特性降低耦合度

Command是将对行为进行封装的典型模式

Factory是将创建进行封装的模式

从Command模式我也发现设计模式一个通病:好象喜欢将简单的问题复杂化

喜欢在不同类中增加第三者当然这样做有利于代码的健壮性 可维护性 还有复用性

如何使用?

具体的Command模式代码各式各样因为如何封装命令不同系统有不同的做法

下面事例是将命令封装在一个Collection的List中任何对象一旦加入List中实际上装入了一个封闭的黑盒中

对象的特性消失了只有取出时才有可能模糊的分辨出:

典型的Command模式需要有一个接口接口中有一个统一的方法这就是将命令/请求封装为对象:

public interface Command {

public abstract void execute ( );

}

具体不同命令/请求代码是实现接口Command下面有三个具体命令

public class Engineer implements Command {

public void execute( ) {

//do Engineers command

}

}

public class Programmer implements Command {

public void execute( ) {

//do programmers command

}

}

public class Politician implements Command {

public void execute( ) {

//do Politicians command

}

}

按照通常做法我们就可以直接调用这三个Command但是使用Command模式

我们要将他们封装起来扔到黑盒子List里去:

public class producer{

public static List produceRequests() {

List queue = new ArrayList();

queueadd( new DomesticEngineer() );

queueadd( new Politician() );

queueadd( new Programmer() );

return queue;

}

}

这三个命令进入List中后已经失去了其外表特征以后再取出

也可能无法分辨出谁是Engineer 谁是Programmer了看下面如何调用Command模式:

public class TestCommand {

public static void main(String[] args) {

List queue = ProducerproduceRequests();

for (Iterator it = erator(); ithasNext(); )

//取出List中东东其他特征都不能确定只能保证一个特征是%正确

// 他们至少是接口Command的儿子所以强制转换类型为接口Command

((Command)itnext())execute();

}

}

由此可见调用者基本只和接口打交道不合具体实现交互这也体现了一个原则

面向接口编程这样以后增加第四个具体命令时就不必修改调用者TestCommand中的代码了

理解了上面的代码的核心原理在使用中就应该各人有自己方法了特别是在如何分离调用者和具体命令上

有很多实现方法上面的代码是使用从List过一遍的做法这种做法只是为了演示

使用Command模式的一个好理由还因为它能实现Undo功能

每个具体命令都可以记住它刚刚执行的动作并且在需要时恢复

Command模式在界面设计中应用广泛Java的Swing中菜单命令都是使用Command模式

/***/

行为模式State

State的定义: 不同的状态不同的行为;或者说每个状态有着相应的行为

何时使用?

State模式在实际使用中比较多适合状态的切换因为我们经常会使用If elseif else 进行状态切换

如果针对状态的这样判断切换反复出现我们就要联想到是否可以采取State模式了

不只是根据状态也有根据属性如果某个对象的属性不同对象的行为就不一样

这点在数据库系统中出现频率比较高我们经常会在一个数据表的尾部

加上property属性含义的字段用以标识记录中一些特殊性质的记录

这种属性的改变(切换)又是随时可能发生的就有可能要使用State

是否使用?

在实际使用类似开关一样的状态切换是很多的但有时并不是那么明显取决于你的经验和对系统的理解深度

这里要阐述的是开关切换状态 一般的状态判断是有一些区别的

一般的状态判断也是有 ifelseif结构例如:

if (which==) state=hello;

else if (which==) state=hi;

else if (which==) state=bye;

这是一个 一般的状态判断state值的不同是根据which变量来决定的which和state没有关系如果改成:

if (stateeuqals(bye)) state=hello;

else if (stateeuqals(hello)) state=hi;

else if (stateeuqals(hi)) state=bye;

这就是 开关切换状态是将state的状态从hello切换到hi

再切换到bye;在切换到hello好象一个旋转开关这种状态改变就可以使用State模式了

如果单纯有上面一种将hello>hi>bye>hello这一个方向切换

也不一定需要使用State模式因为State模式会建立很多子类复杂化

但是如果又发生另外一个行为:将上面的切换方向反过来切换或者需要任意切换就需要State了

请看下例:

public class Context{

private Color state=null;

public void push(){

//如果当前red状态 就切换到blue

if (state==Colorred) state=Colorblue;

//如果当前blue状态 就切换到green

else if (state==Colorblue) state=Colorgreen;

//如果当前black状态 就切换到red

else if (state==Colorblack) state=Colorred;

//如果当前green状态 就切换到black

else if (state==Colorgreen) state=Colorblack;

Sample sample=new Sample(state);

sampleoperate();

}

public void pull(){

//与push状态切换正好相反

if (state==Colorgreen) state=Colorblue;

else if (state==Colorblack) state=Colorgreen;

else if (state==Colorblue) state=Colorred;

else if (state==Colorred) state=Colorblack;

Sample sample=new Sample(state);

sampleoperate();

}

}

在上例中我们有两个动作push推和pull拉这两个开关动作改变了Context颜色

至此我们就需要使用State模式优化它

另外注意:但就上例state的变化只是简单的颜色赋值这个具体行为是很简单的

State适合巨大的具体行为因此在就本例实际使用中也不一定非要使用State模式

这会增加子类的数目简单的变复杂

例如: 银行帐户 经常会在Open 状态和Close状态间转换

例如: 经典的TcpConnection Tcp的状态有创建 侦听 关闭三个

并且反复转换其创建 侦听 关闭的具体行为不是简单一两句就能完成的适合使用State

例如:信箱POP帐号 会有四种状态 start HaveUsername Authorized quit

每个状态对应的行为应该是比较大的适合使用State

例如:在工具箱挑选不同工具可以看成在不同工具中切换适合使用State

如具体绘图程序用户可以选择不同工具绘制方框 直线 曲线这种状态切换可以使用State

如何使用

State需要两种类型实体参与:

state manager 状态管理器 就是开关 如上面例子的Context实际就是一个state manager

在state manager中有对状态的切换动作

用抽象类或接口实现的父类不同状态就是继承这个父类的不同子类

以上面的Context为例我们要修改它建立两个类型的实体

第一步: 首先建立一个父类:

public abstract class State{

public abstract void handlepush(Context c);

public abstract void handlepull(Context c);

public abstract void getcolor();

}

父类中的方法要对应state manager中的开关行为在state manager中

本例就是Context中有两个开关动作push推和pull拉

那么在状态父类中就要有具体处理这两个动作:handlepush() handlepull();

同时还需要一个获取push或pull结果的方法getcolor()

下面是具体子类的实现:

public class BlueState extends State{

public void handlepush(Context c){

//根据push方法如果是blue状态的切换到green ;

csetState(new GreenState());

}

public void handlepull(Context c){

//根据pull方法如果是blue状态的切换到red ;

csetState(new RedState());

}

public abstract void getcolor(){ return (Colorblue)}

}

同样 其他状态的子类实现如blue一样

第二步: 要重新改写State manager 也就是本例的Context:

public class Context{

private Sate state=null; //我们将原来的 Color state 改成了新建的State state;

//setState是用来改变state的状态 使用setState实现状态的切换

pulic void setState(State state){

thisstate=state;

}

public void push(){

//状态的切换的细节部分在本例中是颜色的变化已经封装在子类的handlepush中实现这里无需关心

statehandlepush(this);

//因为sample要使用state中的一个切换结果使用getColor()

Sample sample=new Sample(stategetColor());

sampleoperate();

}

public void pull(){

statehandlepull(this);

Sample sample=new Sample(stategetColor());

sampleoperate();

}

}

至此我们也就实现了State的refactorying过程

以上只是相当简单的一个实例在实际应用中handlepush或handelpull的处理是复杂的

/***/

行为模式Strategy

Strategy是属于设计模式中 对象行为型模式主要是定义一系列的算法把这些算法一个个封装成单独的类

Stratrgy应用比较广泛比如 公司经营业务变化图

可能有两种实现方式一个是线条曲线一个是框图(bar)这是两种算法可以使用Strategy实现

这里以字符串替代为例 有一个文件我们需要读取后希望替代其中相应的变量

然后输出关于替代其中变量的方法可能有多种方法这取决于用户的要求所以我们要准备几套变量字符替代方案

首先我们建立一个抽象类RepTempRule 定义一些公用变量和方法:

public abstract class RepTempRule{

protected String oldString=;

public void setOldString(String oldString){

thisoldString=oldString;

}

protected String newString=;

public String getNewString(){

return newString;

}

public abstract void replace() throws Exception;

}

在RepTempRule中 有一个抽象方法abstract需要继承明确这个replace里其实是替代的具体方法

我们现在有两个字符替代方案

将文本中aaa替代成bbb;

将文本中aaa替代成ccc;

对应的类分别是RepTempRuleOne RepTempRuleTwo

public class RepTempRuleOne extends RepTempRule{

public void replace() throws Exception{

//replaceFirst是jdk新特性

newString=oldStringreplaceFirst(aaa bbbb)

Systemoutprintln(this is replace one);

}

}

public class RepTempRuleTwo extends RepTempRule{

public void replace() throws Exception{

newString=oldStringreplaceFirst(aaa ccc)

Systemoutprintln(this is replace Two);

}

}

第二步我们要建立一个算法解决类用来提供客户端可以自由选择算法

public class RepTempRuleSolve {

private RepTempRule strategy;

public RepTempRuleSolve(RepTempRule rule){

thisstrategy=rule;

}

public String getNewContext(Site siteString oldString) {

return strategyreplace(siteoldString);

}

public void changeAlgorithm(RepTempRule newAlgorithm) {

strategy = newAlgorithm;

}

}

调用如下:

public class test{

public void testReplace(){

//使用第一套替代方案

RepTempRuleSolve solver=new RepTempRuleSolve(new RepTempRuleSimple());

solvergetNewContext(sitecontext);

//使用第二套

solver=new RepTempRuleSolve(new RepTempRuleTwo());

solvergetNewContext(sitecontext);

}

}

我们达到了在运行期间可以自由切换算法的目的

实际整个Strategy的核心部分就是抽象类的使用使用Strategy模式可以在用户需要变化时修改量很少而且快速

Strategy和Factory有一定的类似Strategy相对简单容易理解并且可以在运行时刻自由切换

Factory重点是用来创建对象

Strategy适合下列场合:

以不同的格式保存文件;

以不同的算法压缩文件;

以不同的算法截获图象;

以不同的格式输出同样数据的图形比如曲线 或框图bar等

/***/

行为模式Mediator

Mediator定义:

用一个中介对象来封装一系列关于对象交互行为

为何使用Mediator?

各个对象之间的交互操作非常多;每个对象的行为操作都依赖彼此对方

修改一个对象的行为同时会涉及到修改很多其他对象的行为如果使用Mediator模式

可以使各个对象间的耦合松散只需关心和 Mediator的关系

使多对多的关系变成了一对多的关系可以降低系统的复杂性提高可修改扩展性

如何使用?

首先 有一个接口用来定义成员对象之间的交互联系方式:

public interface Mediator { }

Meiator具体实现真正实现交互操作的内容:

public class ConcreteMediator implements Mediator {

//假设当前有两个成员

private ConcreteColleague colleague = new ConcreteColleague();

private ConcreteColleague colleague = new ConcreteColleague();

}

再看看另外一个参与者:成员因为是交互行为都需要双方提供一些共同接口

这种要求在Visitor Observer等模式中都是相同的

public class Colleague {

private Mediator mediator;

public Mediator getMediator() {

return mediator;

}

public void setMediator( Mediator mediator ) {

diator = mediator;

}

}

public class ConcreteColleague { }

public class ConcreteColleague { }

每个成员都必须知道Mediator并且和 Mediator联系而不是和其他成员联系

至此Mediator模式框架完成可以发现Mediator模式规定不是很多

大体框架也比较简单但实际使用起来就非常灵活

Mediator模式在事件驱动类应用中比较多例如界面设计GUI;聊天消息传递等

在聊天应用中需要有一个MessageMediator专门负责request/reponse之间任务的调节

MVC是JEE的一个基本模式View Controller是一种Mediator它是Jsp和服务器上应用程序间的Mediator

/***/

行为模式Interpreter

Interpreter定义:

定义语言的文法 并且建立一个解释器来解释该语言中的句子

Interpreter似乎使用面不是很广它描述了一个语言解释器是如何构成的

在实际应用中我们可能很少去构造一个语言的文法我们还是来简单的了解一下:

首先要建立一个接口用来描述共同的操作

public interface AbstractExpression {

void interpret( Context context );

}

再看看包含解释器之外的一些全局信息

public interface Context { }

AbstractExpression的具体实现分两种:终结符表达式和非终结符表达式:

public class TerminalExpression implements AbstractExpression {

public void interpret( Context context ) { }

}

对于文法中没一条规则非终结符表达式都必须的:

public class NonterminalExpression implements AbstractExpression {

private AbstractExpression successor;

public void setSuccessor( AbstractExpression successor ) {

thissuccessor = successor;

}

public AbstractExpression getSuccessor() {

return successor;

}

public void interpret( Context context ) { }

}

/***/

行为模式Visitor

Visitor定义

作用于某个对象群中各个对象的操作 它可以使你在不改变这些对象本身的情况下定义作用于这些对象的新操作

在Java中Visitor模式实际上是分离了collection结构中的元素和对这些元素进行操作的行为

为何使用Visitor?

Java的Collection(包括Vector和Hashtable)是我们最经常使用的技术

可是Collection好象是个黑色大染缸本来有各种鲜明类型特征的对象一旦放入后再取出时这些类型就消失了

那么我们势必要用If来判断如:

Iterator iterator = erator()

while (iteratorhasNext()) {

Object o = iteratornext();

if (o instanceof Collection)

messyPrintCollection((Collection)o);

else if (o instanceof String)

Systemoutprintln(+otoString()+);

else if (o instanceof Float)

Systemoutprintln(otoString()+f);

else

Systemoutprintln(otoString());

}

在上例中我们使用了 instanceof来判断 o的类型

很显然这样做的缺点代码If else if 很繁琐我们就可以使用Visitor模式解决它

如何使用Visitor?

针对上例我们设计一个接口visitor访问者:

public interface Visitor

{

public void visitCollection(Collection collection);

public void visitString(String string);

public void visitFloat(Float float);

}

在这个接口中将我们认为Collection有可能的类的类型放入其中

有了访问者我们需要被访问者被访问者就是我们Collection的每个元素Element

我们要为这些Element定义一个可以接受访问的接口(访问和被访问是互动的

只有访问者被访问者如果表示不欢迎访问者就不能访问)

我们定义这个接口叫Visitable用来定义一个Accept操作也就是说让Collection每个元素具备可访问性

public interface Visitable

{

public void accept(Visitor visitor);

}

好了有了两个接口我们就要定义他们的具体实现(Concrete class):

public class ConcreteElement implements Visitable

{

private String value;

public ConcreteElement(String string) {

value = string;

}

//定义accept的具体内容 这里是很简单的一句调用

public void accept(Visitor visitor) {

visitorvisitString(this);

}

}

再看看访问者的Concrete实现:

public class ConcreteVisitor implements Visitor

{

//在本方法中我们实现了对Collection的元素的成功访问

public void visitCollection(Collection collection) {

Iterator iterator = erator()

while (iteratorhasNext()) {

Object o = iteratornext();

if (o instanceof Visitable)

((Visitable)o)accept(this);

}

public void visitString(String string) {

Systemoutprintln(+string+);

}

public void visitFloat(Float float) {

Systemoutprintln(floattoString()+f);

}

}

在上面的visitCollection我们实现了对Collection每个元素访问

只使用了一个判断语句只要判断其是否可以访问

至此我们完成了Visitor模式基本架构

使用Visitor模式的前提

对象群结构中(Collection) 中的对象类型很少改变也就是说访问者的身份类型很少改变

如上面中Visitor中的类型很少改变如果需要增加新的操作比如上例中我们在ConcreteElement具体实现外

还需要新的ConcreteElement ConcreteElement

可见使用Visitor模式是有前提的在两个接口Visitor和Visitable中

确保Visitor很少变化变化的是Visitable这样使用Visitor最方便

如果Visitor也经常变化 也就是说对象群中的对象类型经常改变一般建议是

不如在这些对象类中逐个定义操作但是Java的Reflect技术解决了这个问题

/***/

               

上一篇:Java中多线程之间可以通过接口来实现信息共享

下一篇:初学Java多线程:使用Synchronized块同步方法