文章
· 九月 25, 2022 阅读大约需 78 分钟

IRIS与Caché的23种设计模式

IRIS的23种设计模式

第一章 Caché 设计模式 简单工厂模式

定义

  • 简单工厂模式属于创建型模式,又可称为静态工厂模式,这是由一个工厂对象决定创建出哪一种产品类的实例

使用场景

  • 工厂类负责创建的对象比较少
  • 客户只需知道传入工厂类的参数,而无须关心创建对象的逻辑

优点

  • 使用户根据参数获得对一个的类实例,避免了直接实例化类型,降低了耦合性
  • 能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类:客户类往往有多个,如果不使用简单工厂,那么所有的客户类都要知道所有子类的细节。而且一旦子类发生改变,所有的客户类都要进行修改

缺点

  • 可实例化的类型在编译期间已经被确定。
  • 如果增加新类型,则需要修改工厂,这违背了开放封闭原则
  • 子类多过不适合使用

示例

  • 用Caché 实现一个计算器控制台程序,要求输入两个数和运算符号。

可能会写成这样:

/// w ##class(PHA.YX.Design.Program).GetResult() 
ClassMethod GetResult()
{
    READ "输入第一个数字:", numberA,!
    READ "输入操作符:", operate,!
    READ "输入第二个数字:", numberB,!

    s result = ""
    i operate = "+" d
    .s result = numberA + numberB
    i operate = "-" d
    .s result = numberA - numberB
    i operate = "*" d
    .s result = numberA * numberB
    i operate = "/" d
    .s result = numberA / numberB

    q result
}

缺点:
1. if表达式每次都需要判断,等于计算机做了三次无用功。
2. 如果除法,输入了0,除法会报错。

DHC-APP> w ##class(PHA.YX.Design.Program).GetResult()
输入第一个数字:1
输入操作符:/
输入第二个数字:1.5
.6666666666666666667
DHC-APP> w ##class(PHA.YX.Design.Program).GetResult()
输入第一个数字:1
输入操作符:/
输入第二个数字:0

 .s result = numberA / numberB
  ^
<DIVIDE>zGetResult+12^PHA.YX.Design.Program.1
  • 如果改成swtich($case)

可能会写成这样:


/// w ##class(PHA.YX.Design.Program).GetResultCase() ClassMethod GetResultCase() { READ "输入第一个数字:", numberA,! READ "输入操作符:", operate,! READ "输入第二个数字:", numberB,! s result= $case(operate, "+" : numberA + numberB, "-" : numberA + numberB, "*" : numberA + numberB, "/" : numberA + numberB) q result }

缺点:
1. 如果加减乘除方法非常大,每次修改其中一个都会编译其他三个运算方法。
2. 增加错误修改,可能日后修改代码修改错。

结构图

image

完整示例

  • 利用面相对象的继承和多态。
  • 增加一个幂次运算。
  • 由此例子举一反三。
  • 改修运算,只需要去对应的类去修改就好了。只编译对应的类。

注意

  • Operation类的numberA,numberB为私有字段属性,其他类无法调用。InitialExpression设置初始值。
  • Operation类的numberA,numberB字段,Get,Set方法中的属性,要用i%property,否则爆出递归错误。
  • Operation类GetResult()为抽象方法,子类需要重新实现。
  • Program类SimpleFactory()方法,要显示生命oper类型,否则无法用“.”语法。
  • OperationFactory类返回的是Operation类对象,这里及是多态。
  • 其他操作类均为继承类Operation类的子类,重写Operation类GetResult()方法。

调用

Class PHA.YX.Design.Program Extends %RegisteredObject
{

/// w ##class(PHA.YX.Design.Program).SimpleFactory() 
ClassMethod SimpleFactory()
{
    READ "输入第一个数字:", numberA,!
    READ "输入操作符:", operate,!
    READ "输入第二个数字:", numberB,!

    # dim oper as PHA.YX.Design.SimpleFactory.Operation 
    s oper = ##class(PHA.YX.Design.SimpleFactory.OperationFactory).CreateOperate(operate)
    d oper.numberASet(numberA)
    d oper.numberBSet(numberB) 
    s result = oper.GetResult()
    q result
}

}

输出

DHC-APP>w ##class(PHA.YX.Design.Program).SimpleFactory()
输入第一个数字:1
输入操作符:+
输入第二个数字:1
2
DHC-APP>w ##class(PHA.YX.Design.Program).SimpleFactory()
输入第一个数字:2
输入操作符:-
输入第二个数字:3
-1
DHC-APP>w ##class(PHA.YX.Design.Program).SimpleFactory()
输入第一个数字:6
输入操作符:*
输入第二个数字:3
18
DHC-APP>w ##class(PHA.YX.Design.Program).SimpleFactory()
输入第一个数字:1
输入操作符:/
输入第二个数字:1.5
0.6666666666666666667
DHC-APP>w ##class(PHA.YX.Design.Program).SimpleFactory()
输入第一个数字:1
输入操作符:/
输入第二个数字:0
除数不能为0。
DHC-APP>w ##class(PHA.YX.Design.Program).SimpleFactory()
输入第一个数字:3
输入操作符:**
输入第二个数字:3
27

工厂类

Class PHA.YX.Design.SimpleFactory.OperationFactory Extends %RegisteredObject
{

ClassMethod CreateOperate(operate As %String) As Operation
{
    # dim oper as PHA.YX.Design.SimpleFactory.Operation 

    s oper = $case(operate,
                "+" : ##class(PHA.YX.Design.SimpleFactory.OperationAdd).%New(),
                "-" : ##class(PHA.YX.Design.SimpleFactory.OperationSub).%New(),
                "*" : ##class(PHA.YX.Design.SimpleFactory.OperationMul).%New(),
                "/" : ##class(PHA.YX.Design.SimpleFactory.OperationDiv).%New(),
                "**" : ##class(PHA.YX.Design.SimpleFactory.OperationIndex).%New())
    q oper
}

}

运算类

Class PHA.YX.Design.SimpleFactory.Operation Extends %RegisteredObject
{

Property numberA As %Decimal [ InitialExpression = 0, Private ];

Method numberAGet() As %String [ ServerOnly = 1 ]
{
    Quit i%numberA
}

Method numberASet(Arg As %String) As %Status [ ServerOnly = 1 ]
{
    s i%numberA = Arg
    Quit $$$OK
}

Property numberB As %Decimal [ InitialExpression = 0, Private ];

Method numberBGet() As %String [ ServerOnly = 1 ]
{
    Quit i%numberB
}

Method numberBSet(Arg As %String) As %Status [ ServerOnly = 1 ]
{
    s i%numberB = Arg
    Quit $$$OK
}

Method GetResult() [ Abstract ]
{
    s result = 0
    q result
}

}

加法类

Class PHA.YX.Design.SimpleFactory.OperationAdd Extends PHA.YX.Design.SimpleFactory.Operation
{

Method GetResult()
{
    s reulst = 0
    s reulst = ..numberAGet() + ..numberBGet()
    q reulst
}

}

减法类

Class PHA.YX.Design.SimpleFactory.OperationSub Extends PHA.YX.Design.SimpleFactory.Operation
{

Method GetResult()
{
    s reulst = 0
    s reulst = ..numberAGet() - ..numberBGet()
    q reulst
}

}

乘法类

Class PHA.YX.Design.SimpleFactory.OperationMul Extends PHA.YX.Design.SimpleFactory.Operation
{

Method GetResult()
{
    s reulst = 0
    s reulst = ..numberAGet() * ..numberBGet()
    q reulst
}

}

除法类

Class PHA.YX.Design.SimpleFactory.OperationDiv Extends PHA.YX.Design.SimpleFactory.Operation
{

Method GetResult()
{
    s reulst = 0
    q:..numberBGet()=0 "除数不能为0。"

    s reulst = ..numberAGet() / ..numberBGet()
    q reulst
}

}

指数类

Class PHA.YX.Design.SimpleFactory.OperationIndex Extends PHA.YX.Design.SimpleFactory.Operation
{

Method GetResult()
{
    s reulst = 0

    s reulst = ..numberAGet() ** ..numberBGet()
    q reulst
}

}

思考

厂家可以生产各种品牌电脑,例如hp,lenovo,asus等品牌工简单工厂模式如何实现。感兴趣的同学实现后可以发我一起参考下。

第二章 Caché 设计模式 策略模式

定义

  • 策略模式是一种定义一系列算法的方法,从概念上来看,所有这些类算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。

使用场景

  • 对客户隐藏具体策略的实现细节,彼此完全独立
  • 针对同意类型问题的多种处理方式,仅仅是具体行为有差别时
  • 在一个类中定义了很多行为,而且这些行为在这个类里的操作以多个条件语句的形式出现

优点

  • 简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试
  • 使用策略模式一避免使用多重条件语句,多重条件语句不易维护而且容易出错。消除过多的if else嵌套
  • 易于拓展。当需要添加一个策略时,只需要实现接口就可以。

缺点

  • 每一个策略都是一个类,复用性小,如果策略过多,类的数量会增多
  • 上层模块必须知道有哪些策略,才能够使用这些策略,这与迪米特原则相违背

简单工厂模式与策略模式的区别

  • 工厂模式主要是返回的接口实现类的实例化对象,最后返回的结果是接口实现类中的方法。
  • 策略模式是在实例化策略模式的时候已经创建好了,我们可以再策略模式中随意的拼接重写方法。
  • 而工厂模式是不管方法的拼接这些的,他只关注最后的结果,不注重过程,而策略模式注重的是过程。
  • 工厂模式可以做到的事情,策略模式都可以做到。
  • 策略模式可以做到的事情,工厂模式也可以做到,只是会变得麻烦。
  • 工厂模式中只管生产实例,具体怎么使用工厂实例由调用方决定,策略模式是将生成实例的使用策略放在策略类中配置后才提供调用方使用。
  • 工厂模式调用方可以直接调用工厂实例的方法属性等,策略模式不能直接调用实例的方法属性,需要在策略类中封装策略后调用。

示例:
可以随意组合招数,增加三次打击方法ThreeFighting()。 而 工厂方法则需要调用三次。

Class PHA.YX.Design.Strategy.Context Extends %RegisteredObject
{

Property mFightingStrategy As PHA.YX.Design.Strategy.FightingStrategy;

Method %OnNew(fightingStrategy As PHA.YX.Design.Strategy.FightingStrategy) As %Status [ Private, ServerOnly = 1 ]
{
    s ..mFightingStrategy=fightingStrategy
    Quit $$$OK
}

Method Fighting()
{
    d ..mFightingStrategy.Fighting()
}

Method ThreeFighting()
{
    d ..mFightingStrategy.Fighting()
    d ..mFightingStrategy.Fighting()
    d ..mFightingStrategy.Fighting()
}

}
/// d ##class(PHA.YX.Design.Program).Strategy() 
ClassMethod Strategy()
{
    #dim context as PHA.YX.Design.Strategy.Context
    s context = ##class(PHA.YX.Design.Strategy.Context).%New(##class(PHA.YX.Design.Strategy.CommonRivalStrategy).%New())
    d context.Fighting()
    s context = ##class(PHA.YX.Design.Strategy.Context).%New(##class(PHA.YX.Design.Strategy.StrongRivalStrategy).%New())
    d context.Fighting()
    s context = ##class(PHA.YX.Design.Strategy.Context).%New(##class(PHA.YX.Design.Strategy.WeakRivalStrategy).%New())
    d context.Fighting()
    d context.ThreeFighting()
    q ""
}

结构图

image

完整示例

描述

  • 根据不同的程度的对手,指定不同的方案。

定义策略接口

Class PHA.YX.Design.Strategy.FightingStrategy [ Abstract ]
{

Method Fighting() [ Abstract ]
{
}

}

具体策略实现

Class PHA.YX.Design.Strategy.StrongRivalStrategy Extends (%RegisteredObject, PHA.YX.Design.Strategy.FightingStrategy)
{

Method Fighting()
{
    w "遇到了强大的对象,张无忌使用乾坤大挪移",!
}

}
Class PHA.YX.Design.Strategy.CommonRivalStrategy Extends (%RegisteredObject, PHA.YX.Design.Strategy.FightingStrategy)
{

Method Fighting()
{
    w "遇到了普通的对象,张无忌使用圣火令",!
}

}
Class PHA.YX.Design.Strategy.WeakRivalStrategy Extends (%RegisteredObject, PHA.YX.Design.Strategy.FightingStrategy)
{

Method Fighting()
{
    w "遇到了较弱的对象,张无忌使用太极剑法",!
}

}

上下文策略

Class PHA.YX.Design.Strategy.Context Extends %RegisteredObject
{

Property mFightingStrategy As PHA.YX.Design.Strategy.FightingStrategy;

Method %OnNew(fightingStrategy As PHA.YX.Design.Strategy.FightingStrategy) As %Status [ Private, ServerOnly = 1 ]
{
    s ..mFightingStrategy=fightingStrategy
    Quit $$$OK
}

Method Fighting()
{
    d ..mFightingStrategy.Fighting()
}

}

调用

Class PHA.YX.Design.Program Extends %RegisteredObject
{
/// d ##class(PHA.YX.Design.Program).Strategy() 
ClassMethod Strategy()
{
    #dim context as PHA.YX.Design.Strategy.Context
    s context = ##class(PHA.YX.Design.Strategy.Context).%New(##class(PHA.YX.Design.Strategy.CommonRivalStrategy).%New())
    d context.Fighting()
    s context = ##class(PHA.YX.Design.Strategy.Context).%New(##class(PHA.YX.Design.Strategy.StrongRivalStrategy).%New())
    d context.Fighting()
    s context = ##class(PHA.YX.Design.Strategy.Context).%New(##class(PHA.YX.Design.Strategy.WeakRivalStrategy).%New())
    d context.Fighting()
    q ""
}
}

输出

DHC-APP>w ##class(PHA.YX.Design.Program).Strategy()
遇到了普通的对象,张无忌使用圣火令
遇到了强大的对象,张无忌使用乾坤大挪移
遇到了较弱的对象,张无忌使用太极剑法

思考

商场打折,满300减100,打8折,正常收费,满200减50,打5折,折9上在打折9。感兴趣的同学实现后可以发我一起参考下。

第三章 Caché 设计模式 装饰者模式

定义

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

使用场景

  • 在不影响其他对象的情况下,以动态,透明的方式给单个对象添加职责
  • 需要动态地给一个对象增加功能,这些功能可以动态地撤销
  • 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统拓展和维护时

当系统需要新加功能的时候,是向旧的类中添加新的代码,这些新加的代码装饰了原有类的核心职责或主要行为。而新加的东西仅仅是为了满足一些只在某些特定情况下会执行的特殊行为。
装饰者提供了一个非常好解决方案,它把每个要装饰的功能放在单独的类中,并让这个类包装它所要的对象,因此当需要执行特殊行为时,客户代码就可以在运行时根据需要选择地,按顺序地执行装饰功能

优点

  • 通过组合而非继承的方式,动态地拓展一个对象的功能,在运行时选择不同的装饰器,从而实现不同的行为
  • 有效避免了使用继承方式拓展对象功能而带来的灵活性差,子类无限制扩张问题
  • 具体组件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体组件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开放封闭原则”
  • 把类中的装饰功能从类中搬出去,这样可以简化原有的类。

缺点

  • 因为所有对象继承与Compoent,所以如果Compoent内部结构发生改变,则不可避免的影响所有子类(装饰者和被装饰者)
  • 如果基类改变,则势必影响对象的内部
  • 比继承更加灵活激动的特性,也同时意味着装饰者模式比继承更加容易出错,排错也困难
  • 对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐
  • 装饰层数不能过多,否则影响效率

结构图

image

完整示例

描述

一些穿搭搭配,例如T恤,垮裤,球鞋,皮鞋,西装,领带,皮鞋给某个人来装扮。

组件具体实现类

Class PHA.YX.Design.Decorator.Person Extends %RegisteredObject
{

Property name As %String [ Private ];

Method nameGet() As %String [ ServerOnly = 1 ]
{
    Quit i%name
}

Method nameSet(Arg As %String) As %Status [ ServerOnly = 1 ]
{
    s i%name = Arg
    Quit $$$OK
}

Method Show()
{
    w "装扮者" _ ..name,!
    q ""
}

Method %OnNew(name As %String = "") As %Status [ Private, ServerOnly = 1 ]
{

    d ..nameSet(name)
    Quit $$$OK
}

}

抽象装饰者

Class PHA.YX.Design.Decorator.Finery Extends PHA.YX.Design.Decorator.Person
{

Property component As Person [ Private ];

Method Decorate(component As Person)
{
    s $this.component=component
}

Method Show()
{
    i (..component '= "") d 
    .d ..component.Show()
    q ""
}

}

装饰者具体实现类

Class PHA.YX.Design.Decorator.Tshirts Extends Finery
{

Method Show()
{
    d ..Do()
    w "大T恤",!
    d ##super()
    q ""
}

Method Do()
{
    w "穿"
    q ""
}

}

Class PHA.YX.Design.Decorator.BigTrouser Extends Finery
{

Method Show()
{
    w "垮裤",!
    d ##super()
    q ""
}

}

Class PHA.YX.Design.Decorator.Sneakers Extends Finery
{

Method Show()
{
    w "破球鞋",!
    d ##super()
    q ""
}

}

Class PHA.YX.Design.Decorator.LeatherShoes Extends Finery
{

Method Show()
{
    w "皮鞋",!
    d ##super()
    q ""
}

}

Class PHA.YX.Design.Decorator.Tie Extends Finery
{

Method Show()
{
    w "领带",!
    d ##super()
    q ""
}

}

Class PHA.YX.Design.Decorator.Suit Extends Finery
{

Method Show()
{
    w "西装",!
    d ##super()
    q ""
}

}

调用

/// d ##class(PHA.YX.Design.Program).Decorator() 
ClassMethod Decorator()
{
    #import PHA.YX.Design.Decorator
    #dim yx as PHA.YX.Design.Decorator.Person
    s yx = ##class(PHA.YX.Design.Decorator.Person).%New("姚鑫")

    w "第一种装扮",!
    s mSneakers = ##class(PHA.YX.Design.Decorator.Sneakers).%New()
    s mBigTrouser = ##class(PHA.YX.Design.Decorator.BigTrouser).%New()
    s mTshirts = ##class(PHA.YX.Design.Decorator.Tshirts).%New()

    d mSneakers.Decorate(yx)
    d mBigTrouser.Decorate(mSneakers)
    d mTshirts.Decorate(mBigTrouser)
    d mTshirts.Show()

    w !,"第二种装扮",!
    s mLeatherShoes = ##class(PHA.YX.Design.Decorator.LeatherShoes).%New()
    s mTie = ##class(PHA.YX.Design.Decorator.Tie).%New()
    s mSuit = ##class(PHA.YX.Design.Decorator.Suit).%New()

    d mLeatherShoes.Decorate(yx)
    d mTie.Decorate(mLeatherShoes)
    d mSuit.Decorate(mTie)
    d mSuit.Show()

    w !,"第三种装扮",!
    s mLeatherShoes1 = ##class(PHA.YX.Design.Decorator.LeatherShoes).%New()
    s mTie1 = ##class(PHA.YX.Design.Decorator.Tie).%New()
    s mSuit1 = ##class(PHA.YX.Design.Decorator.Suit).%New()
    s mSneakers1 = ##class(PHA.YX.Design.Decorator.Sneakers).%New()
    s mBigTrouser1 = ##class(PHA.YX.Design.Decorator.BigTrouser).%New()
    s mTshirts1 = ##class(PHA.YX.Design.Decorator.Tshirts).%New()
    d mLeatherShoes1.Decorate(yx)
    d mTie1.Decorate(mLeatherShoes1)
    d mSuit1.Decorate(mTie1)
    d mSneakers1.Decorate(mSuit1)
    d mBigTrouser1.Decorate(mSneakers1)
    d mTshirts1.Decorate(mBigTrouser1)
    d mTshirts1.Show()
    q ""
}

DHC-APP>d ##class(PHA.YX.Design.Program).Decorator()
第一种装扮
大T恤
垮裤
破球鞋
装扮者姚鑫

第二种装扮
西装
领带
皮鞋
装扮者姚鑫

第三种装扮
大T恤
垮裤
破球鞋
西装
领带
皮鞋
装扮者姚鑫

思考

杨过本身会全真剑法,洪七公教他打狗棒法,欧阳锋教他蛤蟆功,用装饰者模式来实现一下。感兴趣的同学实现后可以发我一起参考下。

第四章 Caché 设计模式 代理模式

定义

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

类型

  • 静态代理
  • 动态代理(实现的同学联系我)

使用场景

  • 远程代理 为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在与不同地址空间的事实。
  • 虚拟代理 根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。
  • 安全代理 用来控制真实对象访问时的权限。
  • 智能指引 指当调用真实的对象时,代理处理另外一些事。

优点

  • 真实主题类就是实现实际的业务逻辑,不用关心其他非本职的工作。
  • 真实主题类随时都会发生变化;但是因为它实现了公共接口,所以代理类可以不做任何修改就可以使用。

缺点

  • 当接口方法中添加新的方法之后,目标对象和代理对象都要进行维护。

结构图

image

完整示例

抽象主题类

Class PHA.YX.Design.Proxy.Subject
{

Method GiveDolls() [ Abstract ]
{
}

Method GiveFlower() [ Abstract ]
{
}

Method GiveChocolate() [ Abstract ]
{
}

}

真实主题类

Class PHA.YX.Design.Proxy.RealSubject Extends (%RegisteredObject, PHA.YX.Design.Proxy.Subject)
{

Method GiveDolls()
{
    w "送玩具给" _ ..girl.nameGet(),!
}

Method GiveFlower()
{
    w "送花给" _ ..girl.nameGet(),!
}

Method GiveChocolate()
{
    w "送巧克力给" _ ..girl.nameGet(),!
}

Property girl As SchoolGirl;

Method %OnNew(girl As SchoolGirl) As %Status [ Private, ServerOnly = 1 ]
{
    s $this.girl=girl
    Quit $$$OK
}

}

代理类

Class PHA.YX.Design.Proxy.Proxy Extends (%RegisteredObject, PHA.YX.Design.Proxy.Subject)
{

Method GiveDolls()
{
    d ..realSubject.GiveDolls()
}

Method GiveFlower()
{
    d ..realSubject.GiveFlower()
}

Method GiveChocolate()
{
    d ..realSubject.GiveChocolate()
}

Property realSubject As RealSubject;

Method %OnNew(girl As SchoolGirl) As %Status [ Private, ServerOnly = 1 ]
{
    s ..realSubject=##class(RealSubject).%New(girl)
    Quit $$$OK
}

}

对象类

Class PHA.YX.Design.Proxy.SchoolGirl Extends %RegisteredObject
{

Property name As %String [ Private ];

Method nameGet() As %String [ ServerOnly = 1 ]
{
    Quit i%name
}

Method nameSet(Arg As %String) As %Status [ ServerOnly = 1 ]
{
    s i%name = Arg
    Quit $$$OK
}

}

调用

/// d ##class(PHA.YX.Design.Program).Proxy() 
ClassMethod Proxy()
{
    s girl = ##class(PHA.YX.Design.Proxy.SchoolGirl).%New()
    d girl.nameSet("姚鑫")
    s proxy = ##class(PHA.YX.Design.Proxy.Proxy).%New(girl)
    d proxy.GiveChocolate()
    d proxy.GiveDolls()
    d proxy.GiveFlower()
}
DHC-APP>d ##class(PHA.YX.Design.Program).Proxy()
送巧克力给姚鑫
送玩具给姚鑫
送花给姚鑫

思考

小红帮小明去商店买东西。如何用代理实现。感兴趣的同学实现后可以发我一起参考下。

第五章 Caché 设计模式 工厂方法

工厂方法模式实现,客户端需要决定实例化哪一个工厂来实现运算类,选择判断的问题还是存在的,也就是说,工厂方法把简单工厂的内部逻辑判断移到了客户端来进行,你想加功能,本来是改工厂类,现在是修改客户端。

完整示例

结构图

image

抽象对象类

Class PHA.YX.Design.Factory.Computer Extends %RegisteredObject
{

Method Create() [ Abstract ]
{
}

Method Install() [ Abstract ]
{
}

}

实例对象类

Class PHA.YX.Design.Factory.HPComputer Extends Computer
{

Method Create()
{
    w "创建了HP电脑",!
}

Method Install()
{
    w "安装了HP电脑",!
}

}

Class PHA.YX.Design.Factory.LenovoComputer Extends Computer
{

Method Create()
{
    w "创建了Lenovo电脑",!
}

Method Install()
{
    w "安装了Lenovo电脑",!
}

}

抽象工厂类

Class PHA.YX.Design.Factory.IFactory [ Abstract ]
{

ClassMethod CreateComputer() As Computer [ Abstract ]
{
}

}

实例工厂类

Class PHA.YX.Design.Factory.HPFactory Extends (%RegisteredObject, IFactory)
{

ClassMethod CreateComputer() As Computer
{
    q ##class(HPComputer).%New()
}

}

Class PHA.YX.Design.Factory.LenovoFactory Extends (%RegisteredObject, IFactory)
{

ClassMethod CreateComputer() As Computer
{
    q ##class(LenovoComputer).%New()
}

}

调用


/// d ##class(PHA.YX.Design.Program).Factory() ClassMethod Factory() { #dim factory as PHA.YX.Design.Factory.IFactory s factory = ##class(PHA.YX.Design.Factory.HPFactory).%New() #dim hpComputer as PHA.YX.Design.Factory.Computer s hpComputer = factory.CreateComputer() d hpComputer.Create() d hpComputer.Install() s factory = ##class(PHA.YX.Design.Factory.LenovoFactory).%New() #dim lenovoComputer as PHA.YX.Design.Factory.Computer s lenovoComputer = factory.CreateComputer() d lenovoComputer.Create() d lenovoComputer.Install() }
DHC-APP> d ##class(PHA.YX.Design.Program).Factory()
创建了HP电脑
安装了HP电脑
创建了Lenovo电脑
安装了Lenovo电脑

思考

用第一章的计算案例,来改写成工厂模式。感兴趣的同学实现后可以发我一起参考下。

第六章 Caché 设计模式 原型模式

定义

  • 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
  • 原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节。

使用场景

使用原形模式可以解决复杂对象构建资源消耗的问题。也可以用来只读保护。注意其java里继承Cloneable接口。C#继承ICloneable接口。Caché 直接使用%ConstructClone()方法,克隆时不会走构造函数。

优点

  • 一般在初始化的信息不发生变的情况下,克隆是最好饿办法,这即隐藏了对象创建的细节,又对性能是大大的提高。
  • 不用重新初始化对象,而是动态地获得对象运行时的状态。

结构图

image

描述

要求有一个简历类,必须要有姓名,可以设置性别和年龄,可以设置工作经历。最终需要三分简历。

示例

简历类

Class PHA.YX.Design.Prototype.Resume Extends %RegisteredObject
{

Property name As %String [ Private ];

Property sex As %String [ Private ];

Property age As %String [ Private ];

Property timeArea As %String [ Private ];

Property company As %String [ Private ];


Method %OnNew(name As %String) As %Status [ Private, ServerOnly = 1 ]
{
    s $this.name = name
    Quit $$$OK
}

Method SetPersonalInfo(sex As %String, age As %String)
{
    s $this.sex=sex
    s $this.age=age
}

Method SetWorkExperience(timeArea As %String, company As %String)
{
    s $this.timeArea=timeArea
    s $this.company=company
}

Method Display()
{
    w ..name _ " " _ ..sex _ " " _ ..age,!
    w "工作经历:" _ ..timeArea _ " " _ ..company,!
}

}

初级写法

/// d ##class(PHA.YX.Design.Program).PrototypeExamplePrimary() 
ClassMethod PrototypeExamplePrimary()
{
    s mResumeA = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeA.SetPersonalInfo("男","30")
    d mResumeA.SetWorkExperience("2015-2020","XX公司")

    s mResumeB = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeB.SetPersonalInfo("男","30")
    d mResumeB.SetWorkExperience("2015-2020","XX公司")

    s mResumeC = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeC.SetPersonalInfo("男","30")
    d mResumeC.SetWorkExperience("2015-2020","XX公司")

    d mResumeA.Display()
    d mResumeB.Display()
    d mResumeC.Display()
}

DHC-APP>d ##class(PHA.YX.Design.Program).PrototypeExamplePrimary()
姚鑫 男 30
工作经历:2015-2020 XX公司
姚鑫 男 30
工作经历:2015-2020 XX公司
姚鑫 男 30
工作经历:2015-2020 XX公司

缺点

三分简历需要实例化三次,如果需要一百份,就需要实例化一百次,徒增内存。

中级写法

/// d ##class(PHA.YX.Design.Program).PrototypeExampleIntermediate() 
ClassMethod PrototypeExampleIntermediate()
{
    s mResumeA = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeA.SetPersonalInfo("男","30")
    d mResumeA.SetWorkExperience("2015-2020","XX公司")

    # dim mResumeB as PHA.YX.Design.Prototype.Resume
    s mResumeB = mResumeA

    # dim mResumeC as PHA.YX.Design.Prototype.Resume
    s mResumeC = mResumeA

    d mResumeA.Display()
    d mResumeB.Display()
    d mResumeC.Display()
}
DHC-APP>d ##class(PHA.YX.Design.Program).PrototypeExampleIntermediate()
姚鑫 男 30
工作经历:2015-2020 XX公司
姚鑫 男 30
工作经历:2015-2020 XX公司
姚鑫 男 30
工作经历:2015-2020 XX公司

缺点

引用传值,改动其中一处其他复制实例一起改动。把mResumeB设置一下年龄,mResumeC设置一下公司。如下:

/// d ##class(PHA.YX.Design.Program).PrototypeExampleIntermediate() 
ClassMethod PrototypeExampleIntermediate()
{
    s mResumeA = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeA.SetPersonalInfo("男","30")
    d mResumeA.SetWorkExperience("2015-2020","XX公司")

    # dim mResumeB as PHA.YX.Design.Prototype.Resume
    s mResumeB = mResumeA
    d mResumeB.SetPersonalInfo("男","31")

    # dim mResumeC as PHA.YX.Design.Prototype.Resume
    s mResumeC = mResumeA
    d mResumeC.SetWorkExperience("2017-2020","SS公司")
    d mResumeA.Display()
    d mResumeB.Display()
    d mResumeC.Display()
}

运行结果

DHC-APP>d ##class(PHA.YX.Design.Program).PrototypeExampleIntermediate()
姚鑫 男 31
工作经历:2017-2020 SS公司
姚鑫 男 31
工作经历:2017-2020 SS公司
姚鑫 男 31
工作经历:2017-2020 SS公司

高级写法 (浅复制)

使用 .%ConstructClone()方法复制

/// d ##class(PHA.YX.Design.Program).PrototypeExampleSenior() 
ClassMethod PrototypeExampleSenior()
{
    s mResumeA = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeA.SetPersonalInfo("男","30")
    d mResumeA.SetWorkExperience("2015-2020","XX公司")

    # dim mResumeB as PHA.YX.Design.Prototype.Resume
    s mResumeB = mResumeA.%ConstructClone()
    d mResumeB.SetPersonalInfo("男","31")

    # dim mResumeC as PHA.YX.Design.Prototype.Resume
    s mResumeC =mResumeA.%ConstructClone()
    d mResumeC.SetWorkExperience("2017-2020","SS公司")

    d mResumeA.Display()
    d mResumeB.Display()
    d mResumeC.Display()
}
DHC-APP> d ##class(PHA.YX.Design.Program).PrototypeExampleSenior()
姚鑫 男 30
工作经历:2015-2020 XX公司
姚鑫 男 31
工作经历:2015-2020 XX公司
姚鑫 男 30
工作经历:2017-2020 SS公司

浅复制

如果字段是值类型的,则对该字段执行逐位复制,如果字段是引用类型,则复制引用但不复制引用的对象,因此原始对象及其副本引用统一对象。如下增加一个工作经验类的引用字段:

增加一个WorkExperience类

Class PHA.YX.Design.Prototype.WorkExperience Extends %RegisteredObject
{

Property workDate As %String [ Private ];

Method workDateGet() As %String [ ServerOnly = 1 ]
{
    Quit i%workDate
}

Method workDateSet(Arg As %String) As %Status [ ServerOnly = 1 ]
{
    s i%workDate = Arg
    Quit $$$OK
}

Property company As %String [ Private ];

Method companyGet() As %String [ ServerOnly = 1 ]
{
    Quit i%company
}

Method companySet(Arg As %String) As %Status [ ServerOnly = 1 ]
{
    s i%company = Arg
    Quit $$$OK
}

}

PHA.YX.Design.Prototype.Resume 增加一个work字段引用WorkExperience

Property work As WorkExperience [ Private ];

并且在构造方法初始化它

Method %OnNew(name As %String) As %Status [ Private, ServerOnly = 1 ]
{
    s $this.name = name
    s ..work = ##class(WorkExperience).%New()
    Quit $$$OK
}

给对象添加WorkExperience数据方法

Method CloneWorkExperience()
{
    s ..work = ..work.%ConstructClone()
}

输出也修改一下

Method Display()
{
    w ..name _ " " _ ..sex _ " " _ ..age,!
    w "工作经历:" _ ..timeArea _ " " _ ..company,!
    w "工作经历Object:" _ ..work.workDateGet() _ " " _ ..work.companyGet(),!
}

/// d ##class(PHA.YX.Design.Program).PrototypeExampleShallow() 
ClassMethod PrototypeExampleShallow()
{
    s mResumeA = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeA.SetPersonalInfo("男","30")
    d mResumeA.SetWorkExperience("2015-2020","XX公司")
    d mResumeA.SetWorkExperienceObject("2010-2011","腾讯")

    # dim mResumeB as PHA.YX.Design.Prototype.Resume
    s mResumeB = mResumeA.%ConstructClone()
    d mResumeB.SetPersonalInfo("男","31")
    d mResumeB.SetWorkExperienceObject("2011-2012","阿里")

    # dim mResumeC as PHA.YX.Design.Prototype.Resume
    s mResumeC =mResumeA.%ConstructClone()
    d mResumeC.SetWorkExperience("2017-2020","SS公司")
    d mResumeC.SetWorkExperienceObject("2012-2013","百度")

    d mResumeA.Display()
    d mResumeB.Display()
    d mResumeC.Display()
}
DHC-APP> d ##class(PHA.YX.Design.Program).PrototypeExampleShallow()
姚鑫 男 30
工作经历:2015-2020 XX公司
工作经历Object:2012-2013 百度
姚鑫 男 31
工作经历:2015-2020 XX公司
工作经历Object:2012-2013 百度
姚鑫 男 30
工作经历:2017-2020 SS公司
工作经历Object:2012-2013 百度

结果发现。引用的字段并没有重新复制。

深复制

我们在PHA.YX.Design.Prototype.Resume里给work属性,增加一个克隆方法。

Method CloneWorkExperience()
{
    s ..work = ..work.%ConstructClone()
}
/// d ##class(PHA.YX.Design.Program).PrototypeExampleDeep() 
ClassMethod PrototypeExampleDeep()
{
    s mResumeA = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeA.SetPersonalInfo("男","30")
    d mResumeA.SetWorkExperience("2015-2020","XX公司")
    d mResumeA.CloneWorkExperience()
    d mResumeA.SetWorkExperienceObject("2010-2011","腾讯")

    # dim mResumeB as PHA.YX.Design.Prototype.Resume
    s mResumeB = mResumeA.%ConstructClone()
    d mResumeB.SetPersonalInfo("男","31")
    d mResumeB.CloneWorkExperience()
    d mResumeB.SetWorkExperienceObject("2011-2012","阿里")

    # dim mResumeC as PHA.YX.Design.Prototype.Resume
    s mResumeC =mResumeA.%ConstructClone()
    d mResumeC.SetWorkExperience("2017-2020","SS公司")
    d mResumeC.CloneWorkExperience()
    d mResumeC.SetWorkExperienceObject("2012-2013","百度")

    d mResumeA.Display()
    d mResumeB.Display()
    d mResumeC.Display()
}

结果发现,把引用字段work的值也修改成功。

DHC-APP>d ##class(PHA.YX.Design.Program).PrototypeExampleDeep()
姚鑫 男 30
工作经历:2015-2020 XX公司
工作经历Object:2010-2011 腾讯
姚鑫 男 31
工作经历:2015-2020 XX公司
工作经历Object:2011-2012 阿里
姚鑫 男 30
工作经历:2017-2020 SS公司
工作经历Object:2012-2013 百度

完整示例

简历类(复制类)

Class PHA.YX.Design.Prototype.Resume Extends %RegisteredObject
{

Property name As %String [ Private ];

Property sex As %String [ Private ];

Property age As %String [ Private ];

Property timeArea As %String [ Private ];

Property company As %String [ Private ];

Property work As WorkExperience [ Private ];

Method %OnNew(name As %String) As %Status [ Private, ServerOnly = 1 ]
{
    s $this.name = name
    s ..work = ##class(WorkExperience).%New()
    Quit $$$OK
}

Method CloneWorkExperience()
{
    s ..work = ..work.%ConstructClone()
}

Method SetPersonalInfo(sex As %String, age As %String)
{
    s $this.sex=sex
    s $this.age=age
}

Method SetWorkExperience(timeArea As %String, company As %String)
{
    s $this.timeArea=timeArea
    s $this.company=company
}

Method SetWorkExperienceObject(workDate As %String, company As %String)
{
    d ..work.workDateSet(workDate)
    d ..work.companySet(company)
}

Method Display()
{
    w ..name _ " " _ ..sex _ " " _ ..age,!
    w "工作经历:" _ ..timeArea _ " " _ ..company,!
    w "工作经历Object:" _ ..work.workDateGet() _ " " _ ..work.companyGet(),!
}

}

对象类(工作经验类)

Class PHA.YX.Design.Prototype.WorkExperience Extends %RegisteredObject
{

Property workDate As %String [ Private ];

Method workDateGet() As %String [ ServerOnly = 1 ]
{
    Quit i%workDate
}

Method workDateSet(Arg As %String) As %Status [ ServerOnly = 1 ]
{
    s i%workDate = Arg
    Quit $$$OK
}

Property company As %String [ Private ];

Method companyGet() As %String [ ServerOnly = 1 ]
{
    Quit i%company
}

Method companySet(Arg As %String) As %Status [ ServerOnly = 1 ]
{
    s i%company = Arg
    Quit $$$OK
}

}

调用

/// d ##class(PHA.YX.Design.Program).PrototypeExamplePrimary() 
ClassMethod PrototypeExamplePrimary()
{
    s mResumeA = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeA.SetPersonalInfo("男","30")
    d mResumeA.SetWorkExperience("2015-2020","XX公司")

    s mResumeB = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeB.SetPersonalInfo("男","30")
    d mResumeB.SetWorkExperience("2015-2020","XX公司")

    s mResumeC = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeC.SetPersonalInfo("男","30")
    d mResumeC.SetWorkExperience("2015-2020","XX公司")

    d mResumeA.Display()
    d mResumeB.Display()
    d mResumeC.Display()
}

/// d ##class(PHA.YX.Design.Program).PrototypeExampleIntermediate() 
ClassMethod PrototypeExampleIntermediate()
{
    s mResumeA = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeA.SetPersonalInfo("男","30")
    d mResumeA.SetWorkExperience("2015-2020","XX公司")

    # dim mResumeB as PHA.YX.Design.Prototype.Resume
    s mResumeB = mResumeA
    d mResumeB.SetPersonalInfo("男","31")

    # dim mResumeC as PHA.YX.Design.Prototype.Resume
    s mResumeC = mResumeA
    d mResumeC.SetWorkExperience("2017-2020","SS公司")
    d mResumeA.Display()
    d mResumeB.Display()
    d mResumeC.Display()
}

/// d ##class(PHA.YX.Design.Program).PrototypeExampleSenior() 
ClassMethod PrototypeExampleSenior()
{
    s mResumeA = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeA.SetPersonalInfo("男","30")
    d mResumeA.SetWorkExperience("2015-2020","XX公司")

    # dim mResumeB as PHA.YX.Design.Prototype.Resume
    s mResumeB = mResumeA.%ConstructClone()
    d mResumeB.SetPersonalInfo("男","31")

    # dim mResumeC as PHA.YX.Design.Prototype.Resume
    s mResumeC =mResumeA.%ConstructClone()
    d mResumeC.SetWorkExperience("2017-2020","SS公司")

    d mResumeA.Display()
    d mResumeB.Display()
    d mResumeC.Display()
}

/// d ##class(PHA.YX.Design.Program).PrototypeExampleShallow() 
ClassMethod PrototypeExampleShallow()
{
    s mResumeA = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeA.SetPersonalInfo("男","30")
    d mResumeA.SetWorkExperience("2015-2020","XX公司")
    d mResumeA.SetWorkExperienceObject("2010-2011","腾讯")

    # dim mResumeB as PHA.YX.Design.Prototype.Resume
    s mResumeB = mResumeA.%ConstructClone()
    d mResumeB.SetPersonalInfo("男","31")
    d mResumeB.SetWorkExperienceObject("2011-2012","阿里")

    # dim mResumeC as PHA.YX.Design.Prototype.Resume
    s mResumeC =mResumeA.%ConstructClone()
    d mResumeC.SetWorkExperience("2017-2020","SS公司")
    d mResumeC.SetWorkExperienceObject("2012-2013","百度")

    d mResumeA.Display()
    d mResumeB.Display()
    d mResumeC.Display()
}

/// d ##class(PHA.YX.Design.Program).PrototypeExampleDeep() 
ClassMethod PrototypeExampleDeep()
{
    s mResumeA = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeA.SetPersonalInfo("男","30")
    d mResumeA.SetWorkExperience("2015-2020","XX公司")
    d mResumeA.CloneWorkExperience()
    d mResumeA.SetWorkExperienceObject("2010-2011","腾讯")

    # dim mResumeB as PHA.YX.Design.Prototype.Resume
    s mResumeB = mResumeA.%ConstructClone()
    d mResumeB.SetPersonalInfo("男","31")
    d mResumeB.CloneWorkExperience()
    d mResumeB.SetWorkExperienceObject("2011-2012","阿里")

    # dim mResumeC as PHA.YX.Design.Prototype.Resume
    s mResumeC =mResumeA.%ConstructClone()
    d mResumeC.SetWorkExperience("2017-2020","SS公司")
    d mResumeC.CloneWorkExperience()
    d mResumeC.SetWorkExperienceObject("2012-2013","百度")

    d mResumeA.Display()
    d mResumeB.Display()
    d mResumeC.Display()
}

思考

用深复制实现,Java,Python,C#,连接Caché数据库(有连接属性,连接时间),Caché数据库持有SQL数据库连接(有连接属性,连接时间)。感兴趣的同学实现后可以发我一起参考下。

Caché 设计模式 第七章 模版方法模式

定义

定义一个操作中的算法框架,而讲一些步骤延迟到子类中,使得子类不改变一个算法的结构即可重定义算法的某些特定步骤

使用场景

  • 多个子类有共有的方法,并且逻辑基本相同时。
  • 面对重要,复杂的算法,可以把核心算法设计为模版方法,周边相关细节功能则由个子类实现。
  • 需求通过子类来决定父类算法中的某个步骤是否执行,实现子类对父类的反向控制。

优点

  • 模版模式通过把不变的行为搬移到超类,去除了子类中的重复代码。
  • 子类实现算法的某些细节,有助于算法的扩展。
  • 模版方法就是提供了一个很好的代码复用平台。

缺点

  • 每个不同的实现都需要定义一个子类,这会导致类的个数增加,设计更加抽象

特点

当不变的和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现。我们通过模版方法把这些行为搬移到单一的地方,这样就帮助子类摆脱重复饿不变行为的纠缠。

结构图

image

描述

灭绝师太与张三丰对打,分别开启不同的内功,静脉,武功,武器,步伐。

完整示例

创建抽象类,定义完整框架

Class PHA.YX.Design.TemplateMethod.AbstractSwordsMan Extends %RegisteredObject [ Abstract ]
{

Method Fighting() [ Final ]
{
    d ..NeiGong()
    d ..JingMai()
    i ..HasWeapons() d
    .d ..Weapons()
    d ..Moves()
    d ..Hook()
}

Method Hook()
{
}

Method NeiGong() [ Abstract ]
{
}

Method Weapons() [ Abstract ]
{
}

Method Moves() [ Abstract ]
{
}

Method JingMai()
{
    w "开启七经八脉",!
}

Method HasWeapons() As %Boolean
{
    q $$$YES
}

}

具体实现类

Class PHA.YX.Design.TemplateMethod.ZhangSanFeng Extends AbstractSwordsMan
{

Method HasWeapons() As %Boolean
{
    q $$$NO
}

Method Moves()
{
    w "凌波微步",!
}

Method NeiGong()
{
    w "运行九阳神功",!
}

Method Weapons()
{
}

}

Class PHA.YX.Design.TemplateMethod.MieJueShiTai Extends AbstractSwordsMan
{

Method HasWeapons() As %Boolean
{
    q $$$YES
}

Method Hook()
{
    w "倚天剑被夺走,逃跑",!
}

Method Moves()
{
    w "使用隐身",!
}

Method NeiGong()
{
    w "九阴真经",!
}

Method Weapons()
{
    w "使用倚天剑",!
}

}

调用

/// d ##class(PHA.YX.Design.Program).TemplateMethod() 
ClassMethod TemplateMethod()
{
    #dim zsf as PHA.YX.Design.TemplateMethod.AbstractSwordsMan
    s zsf = ##class(PHA.YX.Design.TemplateMethod.ZhangSanFeng).%New()
    d zsf.Fighting()
    w !
    #dim mhst as PHA.YX.Design.TemplateMethod.AbstractSwordsMan
    s mhst = ##class(PHA.YX.Design.TemplateMethod.MieJueShiTai).%New()
    d mhst.Fighting()
}
DHC-APP>d ##class(PHA.YX.Design.Program).TemplateMethod()
运行九阳神功
开启七经八脉
凌波微步

九阴真经
开启七经八脉
使用倚天剑
使用隐身
倚天剑被夺走,逃跑

思考

两个同学做试卷,抽取重复的代码,提炼代码,用模版方法如何实现,并给出调用

Class PHA.YX.Design.TemplateMethod.TestPaperA Extends %RegisteredObject
{

Method TestQuestion1()
{
    w "杨过得到,后来给了郭靖,炼成倚天剑,屠龙刀的玄铁可能是[]a.球墨铸铁 b.马口铁 c.高速合金钢 d.碳素纤维"
    w "答案:b"
}

Method TestQuestion2()
{
    w "杨过,程英,陆无双铲除了情花,造成[]a.使这职务不在害人 b. 使一种珍惜物种灭绝 c.破坏了那个生物圈的生态平衡 d.造成该地区沙漠化"
    w "答案:a"
}

Method TestQuestion3()
{
    w "杨过在绝情谷中了[]毒a.阿司匹林 b.牛黄解毒片 c.情花毒 d.以上全不对"
    w "答案:c"
}

}
Class PHA.YX.Design.TemplateMethod.TestPaperB Extends %RegisteredObject
{

Method TestQuestion1()
{
    w "杨过得到,后来给了郭靖,炼成倚天剑,屠龙刀的玄铁可能是[]a.球墨铸铁 b.马口铁 c.高速合金钢 d.碳素纤维"
    w "答案:d"
}

Method TestQuestion2()
{
    w "杨过,程英,陆无双铲除了情花,造成[]a.使这职务不在害人 b. 使一种珍惜物种灭绝 c.破坏了那个生物圈的生态平衡 d.造成该地区沙漠化"
    w "答案:c"
}

Method TestQuestion3()
{
    w "杨过在绝情谷中了[]毒a.阿司匹林 b.牛黄解毒片 c.情花毒 d.以上全不对"
    w "答案:a"
}

}

感兴趣的同学实现后可以发我一起参考下。

Caché 设计模式 第八章 外观模式

定义

为子系统的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

优点

  • 减少系统的相互依赖,所以的依赖都是对外观类的依赖,与子系统无关。
  • 对用户隐藏了子系统的具体实现,减少用户对子系统的耦合,这样即使具体的子系统发生了变化,用户也感知不到。
  • 加强了安全性,子系统中的方法如果不在外观类中开通,就无法访问到子系统中的方法。

缺点

  • 不符合开放封闭原则,如果业务出现变更,则可能要直接修改外观类

使用场景

  • 构建一个有层次结构的子系统时,使用外观模式定义子系统中每层的入口点。
  • 如果子系统之间是相互依赖的,则可以让其通过外观接口进行通信,减少子系统之间的依赖关系。
  • 子系统往往会因为不断的重构演化而变得越来越复杂,大多数的模式使用也会产生很多很小的类,这给外部调用他们的用户程序带来了使用上的困难。
  • 我们可以使用外观类提供一个简单的接口,对外隐藏子系统的具体实现并隔离变化。
  • 当维护一个遗留的大型系统时,可能这个系统已经非常难以维护和拓展;但因为它含有重要的功能。所以新的需要必须依赖与它,这时可以使用外观类
  • 为设计粗糙或者复杂的遗留代码提供一个简单的接口,让新系统和外观类交互,而外观类负责与遗留的代码进行交互。

何时使用外观模式

  • 首先,在设计初期阶段,应该要有意识的将不同的两个层分离,比如经典的三层架构,就需要考虑在数据访问层和业务逻辑层,业务逻辑层和表示层的层与层之间建立外观facade,这样可以为复杂的子系统提供一个简单的接口,使用耦合大大降低。
  • 其次,在开发阶段,子系统往往因为为不断的重构演化而变得越来越复杂,大多数模式使用时也都会产生很多很小的类,这本是好事,但也给外部调用他们的用户程序带来了使用上的困难,增加外观模式Facade可以提供一个简单的接口,减少他们之间的依赖。
  • 第三,在维护一个遗留的大型系统时,可能这个系统已经非常难以维护和扩展了,但因为它包含很多非常重要的功能,新的需求开发必须要依赖它,此时用外观模式也是非常合适的。
  • 可以为新系统开发一个外观Facade类,来提供设计粗糙或高度复杂的遗留代码的比较清晰简单的接口,让新系统与Facade对象交互,Facade与遗留代码交互所有复杂的工作。

结构图

image

完整示例

子系统类

Class PHA.YX.Design.Facade.SubSystemOne Extends %RegisteredObject
{

Method MethodOne()
{
    w "子系统方法一",!
}

}

Class PHA.YX.Design.Facade.SubSystemTwo Extends %RegisteredObject
{

Method MethodTwo()
{
    w "子系统方法二",!
}

}

Class PHA.YX.Design.Facade.SubSystemThree Extends %RegisteredObject
{

Method MethodThree()
{
    w "子系统方法三",!
}

}

    Class PHA.YX.Design.Facade.SubSystemFour Extends %RegisteredObject
{

Method MethodFour()
{
    w "子系统方法四",!
}

}

外观类

Class PHA.YX.Design.Facade.Facade Extends %RegisteredObject
{

Property mSubSystemOne As SubSystemOne;

Property mSubSystemTwo As SubSystemTwo;

Property mSubSystemThree As SubSystemThree;

Property mSubSystemFour As SubSystemFour;

Method %OnNew() As %Status [ Private, ServerOnly = 1 ]
{
    s ..mSubSystemOne = ##class(SubSystemOne).%New()
    s ..mSubSystemTwo = ##class(SubSystemTwo).%New()
    s ..mSubSystemThree = ##class(SubSystemThree).%New()
    s ..mSubSystemFour = ##class(SubSystemFour).%New()
    Quit $$$OK
}

Method MethodA()
{
    w "MethodA()",!
    d ..mSubSystemOne.MethodOne()
    d ..mSubSystemTwo.MethodTwo()
    d ..mSubSystemThree.MethodThree()
}

Method MethodB()
{
    w "MethodB()",!
    d ..mSubSystemTwo.MethodTwo()
    d ..mSubSystemThree.MethodThree()
    d ..mSubSystemFour.MethodFour()
}

}

调用

/// d ##class(PHA.YX.Design.Program).Facade() 
ClassMethod Facade()
{
    #dim mFacade as PHA.YX.Design.Facade.Facade
    s mFacade = ##class(PHA.YX.Design.Facade.Facade).%New()
    d mFacade.MethodA()
    w !
    d mFacade.MethodB()
}
DHC-APP>d ##class(PHA.YX.Design.Program).Facade()
MethodA()
子系统方法一
子系统方法二
子系统方法三

MethodB()
子系统方法二
子系统方法三
子系统方法四

思考

有一些武林绝学,比如静脉,内功(九阳神功,乾坤大挪移),招式(太极拳,七伤拳,圣火令)。外观随意组合这些招数应对不同的敌人。感兴趣的同学实现后可以发我一起参考下。

Caché 设计模式 第九章 建造者模式

定义

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

优点

  • 使用建造者模式可以使客户端不必知道产品内部组成的细节。
  • 具体的建造者类之间是相互独立的,容易拓展。
  • 由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。

缺点

  • 产生多余的Build对象以及导演类

使用场景

  • 建造者模式是在当创建复杂对象的算法应该独立于该对象的组成部分以及他们的装配方式时适用的模式。
  • 当创建复杂对象的算法应该独立于该对象的组成部分以及他们的装配方式时。
  • 相同的方法,不同的执行顺序,产生不同的事件结果时。
  • 多个部件或零件都可以被装配到一个对象中,但是产生的运行结果又不相同时。
  • 产品类非常复杂,或者产品类种的调用顺序不同而产生了不同的效能。
  • 创建一些复杂的对象时,这些对象的内部组成构件间的建造顺序是稳定的,但是对象的内部组成构件面临着复杂的变化。

结构图

image

完整示例

产品类

Class PHA.YX.Design.Builder.Product Extends %RegisteredObject
{

Property parts As list Of %String;

Method Add(part As %String)
{
    d ..parts.Insert(part)
}

Method Show()
{
    w "产品 创建-----",!
    f i = 1 : 1 : ..parts.Count() d
    .w ..parts.GetAt(i),!
}

}

抽象建造者类

Class PHA.YX.Design.Builder.Builder Extends %RegisteredObject [ Abstract ]
{

Method BuildPartA() [ Abstract ]
{
}

Method BuildPartB() [ Abstract ]
{
}

Method GetResullt() As Product [ Abstract ]
{
}

}

具体建造者类

Class PHA.YX.Design.Builder.ConcreteBuilder1 Extends Builder
{

Property product As Product [ InitialExpression = {##class(Product).%New()}, Private ];

Method BuildPartA()
{
    d ..product.Add("部件A")
}

Method BuildPartB()
{
    d ..product.Add("部件B")
}

Method GetResullt() As Product
{
    q ..product
}

}

Class PHA.YX.Design.Builder.ConcreteBuilder2 Extends Builder
{

Property product As Product [ InitialExpression = {##class(Product).%New()}, Private ];

Method BuildPartA()
{
    d ..product.Add("部件X")
}

Method BuildPartB()
{
    d ..product.Add("部件Y")
}

Method GetResullt() As Product
{
    q ..product
}

}

指挥者类

Class PHA.YX.Design.Builder.Director Extends %RegisteredObject
{

Method Construct(builder As Builder)
{
    d builder.BuildPartA()
    d builder.BuildPartB()
}

}

调用

/// d ##class(PHA.YX.Design.Program).Builder() 
ClassMethod Builder()
{
    s director = ##class(PHA.YX.Design.Builder.Director).%New()
    # dim b1 as PHA.YX.Design.Builder.ConcreteBuilder1
    s b1 = ##class(PHA.YX.Design.Builder.ConcreteBuilder1).%New()
    # dim b2 as PHA.YX.Design.Builder.ConcreteBuilder2
    s b2 = ##class(PHA.YX.Design.Builder.ConcreteBuilder2).%New()

    d director.Construct(b1)
    # dim p1 as PHA.YX.Design.Builder.Product
    s p1 = b1.GetResullt()
    d p1.Show()
    w "--------------",!
    d director.Construct(b2)
    # dim p2 as PHA.YX.Design.Builder.Product
    s p2 = b2.GetResullt()
    d p2.Show()
}

思考

组装一个电脑产品(cpu,键盘,内存),具体建造的过程不可见,如何用建造者模式来实现。
感兴趣的同学实现后可以发我一起参考下。

第十章 Caché 设计模式 观察者模式

定义

定义对象件一种一对多的依赖关系,每当一个对象改变状态时,则所有依赖与它的对象都会得到通知并被自动更新。

使用目的

将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护,扩展和重用都带来不便。

使用场景

  • 关联行为场景,需要注意的是,关联行为是可拆分的,而不是组合关系。
  • 事件多级触发场景。
  • 跨系统的消息交换场景,如消息队列,事件总线的处理机制
  • 当一个对象的改变需要同时改变其他对象时。而且并不知道具有有多少对象有待改变时,应考虑观察者模式。

优点

  • 观察者与被观察者之间是抽象耦合,容易扩展。
  • 方便建立一套触发机制。
  • 观察者所做的工作其实就是在解除耦合。让耦合的双方都依赖于抽象,而不是依赖与具体。从而是的使得各自的变化都不会影响另一边的变化。

缺点

  • 应用观察者模式时需要考虑一下开发效率和运行效率的问题。
  • 程序中包括一个被观察者,多个观察者,开发调试等内容会比较复杂,而且Java中消息的 通知一般是顺序执行的,一个卡顿会影响整体效率,这种一般开启异步子线程。

结构图

image

完整实例

抽象观察者

Class PHA.YX.Design.Observer.Observer Extends %RegisteredObject [ Abstract ]
{

Property name As %String;

Property sub As Subject;

Method %OnNew(name As %String, sub As Subject) As %Status [ Private, ServerOnly = 1 ]
{
    s $this.name=name
    s $this.sub=sub
    Quit $$$OK
}

Method Update() [ Abstract ]
{
}

}


具体观察者

Class PHA.YX.Design.Observer.StockObserver Extends Observer
{

Method %OnNew(name As %String, sub As Subject) As %Status [ Private, ServerOnly = 1 ]
{
    s $this.name=name
    s $this.sub=sub
    Quit $$$OK
}

Method Update()
{
    w ..sub.subjectState _""_ ..name _ ",关闭股票行情,继续工作!"
}

}

Class PHA.YX.Design.Observer.GameObserver Extends Observer
{

Method %OnNew(name As %String, sub As Subject) As %Status [ Private, ServerOnly = 1 ]
{
    s $this.name=name
    s $this.sub=sub
    Quit $$$OK
}

Method Update()
{
    w ..sub.subjectState _""_ ..name _ ",赶紧关闭玩游戏了,继续工作!"
}

}

Class PHA.YX.Design.Observer.StudyObserver Extends Observer
{

Method %OnNew(name As %String, sub As Subject) As %Status [ Private, ServerOnly = 1 ]
{
    s $this.name=name
    s $this.sub=sub
    Quit $$$OK
}

Method Update()
{
    w ..sub.subjectState _""_ ..name _ ",别学习了,继续工作!"
}

}

抽象被观察者

Class PHA.YX.Design.Observer.Observer Extends %RegisteredObject [ Abstract ]
{

Property name As %String;

Property sub As Subject;

Method %OnNew(name As %String, sub As Subject) As %Status [ Private, ServerOnly = 1 ]
{
    s $this.name=name
    s $this.sub=sub
    Quit $$$OK
}

Method Update() [ Abstract ]
{
}

}

具体被观察者

Class PHA.YX.Design.Observer.Boss Extends Subject
{

Property observers As list Of Observer;

Property action As %String;

Method Attach(observer As Observer)
{
    d ..observers.Insert(observer)
}

Method Detach(observer As Observer)
{
    d ..observers.RemoveAt(observer)
}

Method Notify()
{
    f i = 1 : 1 : ..observers.Count() d
    .d ..observers.GetAt(i).Update()
    .w !
}

}

调用

/// d ##class(PHA.YX.Design.Program).Observer() 
ClassMethod Observer()
{
    #dim mBoss as PHA.YX.Design.Observer.Boss
    s mBoss = ##class(PHA.YX.Design.Observer.Boss).%New()

    #dim mStockObserver as PHA.YX.Design.Observer.StockObserver
    s mStockObserver = ##class(PHA.YX.Design.Observer.StockObserver).%New("马玉强", mBoss)

    #dim mGameObserver as PHA.YX.Design.Observer.GameObserver
    s mGameObserver = ##class(PHA.YX.Design.Observer.GameObserver).%New("丁竑莹", mBoss)

    #dim mStudyObserver as PHA.YX.Design.Observer.StudyObserver
    s mStudyObserver = ##class(PHA.YX.Design.Observer.StudyObserver).%New("云海宝", mBoss)

    d mBoss.Attach(mStockObserver)
    d mBoss.Attach(mGameObserver)
    d mBoss.Attach(mStudyObserver)

    s mBoss.subjectState = "老板回来拉!"
    d mBoss.Notify()
    w "删除其中一个",!
    d mBoss.Detach(3)
    d mBoss.Notify()
}

DHC-APP>d ##class(PHA.YX.Design.Program).Observer()
老板回来拉!马玉强,关闭股票行情,继续工作!
老板回来拉!丁竑莹,赶紧关闭玩游戏了,继续工作!
老板回来拉!云海宝,别学习了,继续工作!
删除其中一个
老板回来拉!马玉强,关闭股票行情,继续工作!
老板回来拉!丁竑莹,赶紧关闭玩游戏了,继续工作!

思考
模拟微信和微信用户,通知张无忌结婚了。感兴趣的同学实现后可以发我一起参考下。

第十一章 Caché 设计模式 抽象工厂模式

定义

提供一个创建一系列相关或相互依赖的接口,而无需制定它们具体的类。

优点

  • 相比与简单工厂,没有违背开放封闭原则
  • 可直接创建产品即可

结构图

image

描述

连接数据库SQL和Cache并且每个数据库映射的同张的表的类型是不同的。

完整示例

实体类

Class PHA.YX.Design.AbstractFactory.User Extends %RegisteredObject
{

Property ID As %String;

Method IDGet() As %String [ ServerOnly = 1 ]
{
    Quit i%ID
}

Method IDSet(Arg As %String) As %Status [ ServerOnly = 1 ]
{
    s i%ID = Arg
    Quit $$$OK
}

Property name As %String;

Method nameGet() As %String [ ServerOnly = 1 ]
{
    Quit i%name
}

Method nameSet(Arg As %String) As %Status [ ServerOnly = 1 ]
{
    s i%name = Arg
    Quit $$$OK
}

}

Class PHA.YX.Design.AbstractFactory.Department Extends %RegisteredObject
{

Property ID As %String;

Method IDGet() As %String [ ServerOnly = 1 ]
{
    Quit i%ID
}

Method IDSet(Arg As %String) As %Status [ ServerOnly = 1 ]
{
    s i%ID = Arg
    Quit $$$OK
}

Property name As %String;

Method nameGet() As %String [ ServerOnly = 1 ]
{
    Quit i%name
}

Method nameSet(Arg As %String) As %Status [ ServerOnly = 1 ]
{
    s i%name = Arg
    Quit $$$OK
}

}

抽象产品类

Class PHA.YX.Design.AbstractFactory.IDepartment Extends %RegisteredObject
{

Method Insert(department As Department)
{
}

Method GetDepartment(ID As %Integer)
{
}

}

Class PHA.YX.Design.AbstractFactory.IUser Extends %RegisteredObject
{

Method Insert(user As User)
{
}

Method GetUser(ID As %Integer)
{
}

}

具体产品类

User

Class PHA.YX.Design.AbstractFactory.SqlUser Extends IUser
{

Method Insert(user As User)
{
    w "在SQL中给User表添加一条记录",!
}

Method GetUser(ID As %Integer)
{
    w "在SQL中根据ID得到User表一条记录",!
}

ClassMethod CreatObject()
{
    q ##class(PHA.YX.Design.AbstractFactory.SqlUser).%New()
}

}

Class PHA.YX.Design.AbstractFactory.CacheUser Extends IUser
{

Method Insert(user As User)
{
    w "在Cache中给User表添加一条记录",!
}

Method GetUser(ID As %Integer)
{
    w "在Cache中根据ID得到User表一条记录",!
}

ClassMethod CreatObject()
{
    q ##class(PHA.YX.Design.AbstractFactory.CacheUser).%New()
}

}

Department

Class PHA.YX.Design.AbstractFactory.SqlDepartment Extends IDepartment
{

Method Insert(department As Department)
{
    w "在SQL中给Department表添加一条记录",!
}

Method GetDepartment(ID As %Integer)
{
    w "在SQL中根据ID得到Department表一条记录",!
}

ClassMethod CreatObject()
{
    q ##class(PHA.YX.Design.AbstractFactory.SqlDepartment).%New()
}

}

Class PHA.YX.Design.AbstractFactory.CacheDepartment Extends IDepartment
{

Method Insert(department As Department)
{
    w "在Cache中给Department表添加一条记录",!
}

Method GetDepartment(ID As %Integer)
{
    w "在Cache中根据ID得到Department表一条记录",!
}

ClassMethod CreatObject()
{
    q ##class(PHA.YX.Design.AbstractFactory.CacheDepartment).%New()
}

}

抽象工厂类

  • 因为Caché 系统$反射方法没有创建对象的方法。所以采用了一种讨巧的方式.
    • $classmethod 可以直接调用静态方法。
    • 通过给实体类创建一个静态方法返回该对象的实例。
  • 直接用XECUTE命令直接反射,然后获取引用对象。
  • 配置的字符串可以通过inc文件来制定,符合开放封闭原则,又可配置

注意:本章用的反射+配置实现访问程序

Include PHA.YX.Design.AbstractFactory.Data

Class PHA.YX.Design.AbstractFactory.DataAccess Extends %RegisteredObject
{

Parameter sqlDatabase = "Sql";

Parameter cacheDatabase = "Cache";

ClassMethod CreateUser() As IUser
{
    # dim mUser as PHA.YX.Design.AbstractFactory.IUser
    s classNmae = "PHA.YX.Design.AbstractFactory." _ ..#sqlDatabase _ "User"
    s mUser = $classmethod(classNmae,"CreatObject")
    q mUser
}

ClassMethod CreateDepartment() As IDepartment
{
    # dim mUser as PHA.YX.Design.AbstractFactory.IDepartment
    s classNmae = "PHA.YX.Design.AbstractFactory." _ ..#sqlDatabase _ "Department"
    s mUser = $classmethod(classNmae,"CreatObject")
    q mUser
}

ClassMethod CreateUserTwo() As IUser
{
    # dim mUser as PHA.YX.Design.AbstractFactory.IUser
    s classNmae = "PHA.YX.Design.AbstractFactory." _ $$$database _ "User"
    x ("(mUser) s mUser = ##class(" _ classNmae _ ").%New()", .mUser)
    q mUser
}

}


#define database "Cache"

思考

改写第一章思考生产电脑的例子。感兴趣的同学实现后可以发我一起参考下。

第十二章 Caché 设计模式 状态模式

定义

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

目的

状态模式主要解决是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中。可以把复杂的判断逻辑简单化。

优点

  • 将与特定状态相关的行为局部化,并且将不同状态的行为,分割开来。
  • 将特定的状态相关的形尾都放入一个对象中,由于所有与状态相关的代码都存在与某个ConcreteState中,所以通过定义新的子类可以很容易地增加新的状态和转换。
  • 状态模式通过把各种状态的转移逻辑分布到State的子类之间,来减少相互间依赖。

使用场景

  • 当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式了。

与策略模式区别

  • 策略模式的侧重点是提供不同的方法(策略)。
  • 状态模式的行为是由状态来决定,不同状态有不同的行为。
  • 状态模式的行为是平行的、不可替换的,策略模式是属于对象的行为模式,其行为是彼此独立可相互替换的

结构图

image

描述

程序员每个小时(12,13,17,21,是否下班)描述不同的行为

初级写法

硬编码,面相过程示例:

/// d ##class(PHA.YX.Design.Program).PrimaryState() 
ClassMethod WriteProgram(hour, finish)
{
    i hour < 12  d
    .w "当前时间:" _ hour _ "点 上午工作,精神百倍",!
    e  i hour < 13 d
    .w "当前时间:" _ hour _ "点 饿了,午饭:犯困,午休",!
    e  i hour < 17 d
    .w "当前时间:" _ hour _ "点 下午状态还不错,继续努力",!
    e  d
    .i finish d
    ..w "当前时间:" _ hour _ "点 下班回家了",!
    .e  d
    ..i hour < 21 d
    ...w "当前时间:" _ hour _ "点 加班哦,疲惫",!
    ..e  d
    ...w "当前时间:" _ hour _ "点 不行了,睡觉了",!
}

/// d ##class(PHA.YX.Design.Program).PrimaryState() 
ClassMethod PrimaryState(hour, finish)
{
    d ..WriteProgram(9, 0)
    d ..WriteProgram(10, 0)
    d ..WriteProgram(12, 0)
    d ..WriteProgram(13, 0)
    d ..WriteProgram(14, 0)
    d ..WriteProgram(17, 0)
    d ..WriteProgram(19, 0)
    d ..WriteProgram(22, 0)
}
DHC-APP>d ##class(PHA.YX.Design.Program).PrimaryState()
当前时间:9点 上午工作,精神百倍
当前时间:10点 上午工作,精神百倍
当前时间:12点 饿了,午饭:犯困,午休
当前时间:13点 下午状态还不错,继续努力
当前时间:14点 下午状态还不错,继续努力
当前时间:17点 加班哦,疲惫
当前时间:19点 加班哦,疲惫
当前时间:22点 不行了,睡觉了

完整示例

抽象状态类

Class PHA.YX.Design.State.State Extends %RegisteredObject
{

Method WriteProgram(work As Work) [ Abstract ]
{
}

}

上下文类

Class PHA.YX.Design.State.Work Extends %RegisteredObject
{

Property current As State [ Private ];

Method %OnNew() As %Status [ Private, ServerOnly = 1 ]
{
    s $this.current = ##class(PHA.YX.Design.State.ForenoonState).%New()
    Quit $$$OK
}

Property hour As %Decimal;

Method hourGet() As %Decimal [ ServerOnly = 1 ]
{
    Quit i%hour
}

Method hourSet(Arg As %Decimal) As %Status [ ServerOnly = 1 ]
{
    s i%hour = Arg
    Quit $$$OK
}

Property finish As %Boolean [ InitialExpression = 0, Private ];

Method finishGet() As %Boolean [ ServerOnly = 1 ]
{
    Quit i%finish
}

Method finishSet(Arg As %Boolean) As %Status [ ServerOnly = 1 ]
{
    s i%finish = Arg
    Quit $$$OK
}

Method SetState(state As State)
{
    s $this.current = state
}

Method WriteProgram()
{
    d ..current.WriteProgram($this)
}

}

具体状态类

Class PHA.YX.Design.State.ForenoonState Extends State
{

Method WriteProgram(work As Work)
{
    i work.hourGet() < 12 d
    .w "当前时间:" _ work.hourGet() _ "点 上午工作,精神百倍",!
    e  d
    .d work.SetState(##class(PHA.YX.Design.State.NoonState).%New())
    .d work.WriteProgram()
}

}

Class PHA.YX.Design.State.NoonState Extends State
{

Method WriteProgram(work As Work)
{
    i work.hourGet() < 13 d
    .w "当前时间:" _ work.hourGet() _ "点 饿了,午饭:犯困,午休",!
    e  d
    .d work.SetState(##class(PHA.YX.Design.State.AfternoonState).%New())
    .d work.WriteProgram()
}

}

Class PHA.YX.Design.State.AfternoonState Extends State
{

Method WriteProgram(work As Work)
{
    i work.hourGet() < 17 d
    .w "当前时间:" _ work.hourGet() _ "点 下午状态还不错,继续努力",!
    e  d
    .d work.SetState(##class(PHA.YX.Design.State.EveningState).%New())
    .d work.WriteProgram()
}

}

Class PHA.YX.Design.State.EveningState Extends %RegisteredObject
{

Method WriteProgram(work As Work)
{
    i work.finishGet() d
    .d work.SetState(##class(PHA.YX.Design.State.RestState).%New())
    .d work.WriteProgram()
    e  d
    .i work.hourGet() < 21 d
    ..w "当前时间:" _ work.hourGet() _ "点 加班哦,疲惫",!
    .e  d
    ..d work.SetState(##class(PHA.YX.Design.State.SleepingState).%New())
    ..d work.WriteProgram()
}

}

Class PHA.YX.Design.State.SleepingState Extends State
{

Method WriteProgram(work As Work)
{
    w "当前时间:" _ work.hourGet() _ "点 不行了,睡觉了",!
}

}

Class PHA.YX.Design.State.RestState Extends State
{

Method WriteProgram(work As Work)
{
    w "当前时间:" _ work.hourGet() _ "点 下班回家了",!
}

}

调用

/// d ##class(PHA.YX.Design.Program).State() 
ClassMethod State()
{
    #dim mWork as PHA.YX.Design.State.Work
    s mWork = ##class(PHA.YX.Design.State.Work).%New()
    d mWork.hourSet(9)
    d mWork.WriteProgram()
    d mWork.hourSet(10)
    d mWork.WriteProgram()
    d mWork.hourSet(12)
    d mWork.WriteProgram()
    d mWork.hourSet(13)
    d mWork.WriteProgram()
    d mWork.hourSet(14)
    d mWork.WriteProgram()
    d mWork.hourSet(17)
    d mWork.WriteProgram()

    d mWork.finishSet(1)
    d mWork.WriteProgram()

    d mWork.hourSet(19)
    d mWork.WriteProgram()
    d mWork.hourSet(22)
    d mWork.WriteProgram()
}
DHC-APP>d ##class(PHA.YX.Design.Program).State()
当前时间:9点 上午工作,精神百倍
当前时间:10点 上午工作,精神百倍
当前时间:12点 饿了,午饭:犯困,午休
当前时间:13点 下午状态还不错,继续努力
当前时间:14点 下午状态还不错,继续努力
当前时间:17点 加班哦,疲惫
当前时间:19点 加班哦,疲惫
当前时间:22点 不行了,睡觉了

DHC-APP>d ##class(PHA.YX.Design.Program).State()
当前时间:9点 上午工作,精神百倍
当前时间:10点 上午工作,精神百倍
当前时间:12点 饿了,午饭:犯困,午休
当前时间:13点 下午状态还不错,继续努力
当前时间:14点 下午状态还不错,继续努力
当前时间:17点 加班哦,疲惫
当前时间:17点 下班回家了
当前时间:19点 下班回家了
当前时间:22点 下班回家了

思考

电视机开机时可以调频道,调音量,开机关机。关机时调频道,调音量,开机关机这些状态为不能用。如何来实现。感兴趣的同学实现后可以发我一起参考下。

第十三章 Caché 设计模式 适配器模式

定义

将一个类的接口转换成客户希望另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

使用场景

  • 系统的数据和行为都正确,但接口不符时,我们应该考虑用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配。适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一直的情况。
  • 使用一个已经存在的类,但如果它的接口,也就是它的方法和你的要求又不相同时,就应该考虑用适配器模式,也就是说两个类所做的事情相同或相似,但是具体不同的接口时要使用它。

优点

  • 把接口和类结合,通过适配器可以让接口定义的功能更好的复用。
  • 扩展性好,不光可调用自己开发的功能,还自然的扩展了接口定义的其它功能。

缺点

不适合兼容太多适配器

结构图

image

描述

NBA打篮球有中锋,前锋,后卫,有外籍球员来到美国不会说英语,要用翻译来适配。

完整实例

抽象接口

Class PHA.YX.Design.Adapter.Player Extends %RegisteredObject
{

Property name As %String;

Method Attack()
{
}

Method Defense()
{
}

Method %OnNew(name As %String) As %Status [ Private, ServerOnly = 1 ]
{
    s $this.name = name
    Quit $$$OK
}

}

具体实现接口

Class PHA.YX.Design.Adapter.Forwards Extends Player
{

Method Attack()
{
    w "前锋" _ ..name _ "进攻",!
}

Method Defense()
{
    w "前锋" _ ..name _ "防守",!
}

Method %OnNew(name As %String) As %Status [ Private, ServerOnly = 1 ]
{
    d ##super(name)
    Quit $$$OK
}

}

Class PHA.YX.Design.Adapter.Center Extends Player
{

Method Attack()
{
    w "中锋" _ ..name _ "进攻",!
}

Method Defense()
{
    w "中锋" _ ..name _ "防守",!
}

Method %OnNew(name As %String) As %Status [ Private, ServerOnly = 1 ]
{
    d ##super(name)
    Quit $$$OK
}

}

Class PHA.YX.Design.Adapter.Guards Extends Player
{

Method Attack()
{
    w "后锋" _ ..name _ "进攻",!
}

Method Defense()
{
    w "后锋" _ ..name _ "防守",!
}

Method %OnNew(name As %String) As %Status [ Private, ServerOnly = 1 ]
{
    d ##super(name)
    Quit $$$OK
}

}

被适配类

Class PHA.YX.Design.Adapter.ForeignCenter Extends %RegisteredObject
{

Method ChineseAttack()
{
    w "外籍" _ ..name _ "进攻",!
}

Method ChineseDefense()
{
    w "外籍" _ ..name _ "防守",!
}

Property name As %String;

Method nameGet() As %String [ ServerOnly = 1 ]
{
    Quit i%name
}

Method nameSet(Arg As %String) As %Status [ ServerOnly = 1 ]
{
    s i%name = Arg
    Quit $$$OK
}

}


适配器

Class PHA.YX.Design.Adapter.Translator Extends Player
{

Property mForeignCenter As ForeignCenter [ InitialExpression = {##class(ForeignCenter).%New()} ];

Method Attack()
{
    d ..mForeignCenter.ChineseAttack()
}

Method Defense()
{
    d ..mForeignCenter.ChineseDefense()
}

Method %OnNew(name As %String) As %Status [ Private, ServerOnly = 1 ]
{

    d ##super(name)
    s ..mForeignCenter.name = name
    Quit $$$OK
}

}


调用

/// d ##class(PHA.YX.Design.Program).Adapter() 
ClassMethod Adapter()
{
    #dim b as PHA.YX.Design.Adapter.Forwards
    s b = ##class(PHA.YX.Design.Adapter.Forwards).%New("巴特尔")
    d b.Attack()

    #dim o as PHA.YX.Design.Adapter.Center
    s o = ##class(PHA.YX.Design.Adapter.Center).%New("奥尼尔")
    d o.Defense()

    #dim m as PHA.YX.Design.Adapter.Guards
    s m = ##class(PHA.YX.Design.Adapter.Guards).%New("麦迪")
    d m.Attack()

    #dim ym as PHA.YX.Design.Adapter.Translator
    s ym = ##class(PHA.YX.Design.Adapter.Translator).%New("姚明")
    d ym.Attack()
    d ym.Defense()
}
DHC-APP>d ##class(PHA.YX.Design.Program).Adapter()
前锋巴特尔进攻
中锋奥尼尔防守
后锋麦迪进攻
外籍姚明进攻
外籍姚明防守

思考

有些国家电压110V,我过电压220v,通过适配器模式把110V变成我国能用的电压。感兴趣的同学实现后可以发我一起参考下。

第十四章 Caché 设计模式 备忘录模式

定义

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象回复到原先保存的状态。

使用场景

  • 备忘录模式比较适用于功能比较复杂的,但需要维护或记录属性历史的类,或者需要保存的属性只是众多属性中的一小部分时,Originator可以根据保存的Memento信息还原到前一状态。
  • 如果在某个系统中使用命令模式时,需要实现命令的撤销功能,那么命令模式可以使用备忘录模式来储存可撤销操作的状态。
  • 当角色的状态改变的时候,有可能在这个状态无效,这时候就可以使用暂时储存起来的备忘录将状态复原。

结构图

image

完整示例

角色类

Class PHA.YX.Design.Memento.GameRole Extends %RegisteredObject
{

Property vitality As %Integer [ Private ];

Method vitalityGet() As %Integer [ ServerOnly = 1 ]
{
    Quit i%vitality
}

Method vitalitySet(Arg As %Integer) As %Status [ ServerOnly = 1 ]
{
    s i%vitality = Arg
    Quit $$$OK
}

Property attact As %Integer [ Private ];

Method attactGet() As %Integer [ ServerOnly = 1 ]
{
    Quit i%attact
}

Method attactSet(Arg As %Integer) As %Status [ ServerOnly = 1 ]
{
    s i%attact = Arg
    Quit $$$OK
}

Property defense As %Integer [ Private ];

Method defenseGet() As %Integer [ ServerOnly = 1 ]
{

    Quit i%defense
}

Method defenseSet(Arg As %Integer) As %Status [ ServerOnly = 1 ]
{
    s i%defense = Arg
    Quit $$$OK
}

Method StateDisplay()
{
    w "当前角色状态:",!
    w "体力:" _ ..vitalityGet(),!
    w "攻击力:" _ ..attactGet(),!
    w "防御力:" _ ..defenseGet(),!
    w "",!
}

Method GetInitState()
{
    s ..vitality = 100
    s ..attact = 100
    s ..defense = 100
}

Method Fight()
{
    s ..vitality = 0
    s ..attact = 0
    s ..defense = 0
}

Method SaveState() As RoleStateMemento
{
    q ##class(RoleStateMemento).%New(..vitality, ..attact, ..defense)
}

Method RecoveryState(memento As RoleStateMemento)
{
    s ..vitality = memento.vitalityGet()
    s ..attact = memento.attactGet()
    s ..defense = memento.defenseGet()
}

}

储存类

Class PHA.YX.Design.Memento.RoleStateMemento Extends %RegisteredObject
{

Property vitality As %Integer [ Private ];

Method vitalityGet() As %Integer [ ServerOnly = 1 ]
{
    Quit i%vitality
}

Method vitalitySet(Arg As %Integer) As %Status [ ServerOnly = 1 ]
{
    s i%vitality = Arg
    Quit $$$OK
}

Property attact As %Integer [ Private ];

Method attactGet() As %Integer [ ServerOnly = 1 ]
{
    Quit i%attact
}

Method attactSet(Arg As %Integer) As %Status [ ServerOnly = 1 ]
{
    s i%attact = Arg
    Quit $$$OK
}

Property defense As %Integer [ Private ];

Method defenseGet() As %Integer [ ServerOnly = 1 ]
{

    Quit i%defense
}

Method defenseSet(Arg As %Integer) As %Status [ ServerOnly = 1 ]
{
    s i%defense = Arg
    Quit $$$OK
}

Method %OnNew(vitality, attact, defense) As %Status [ Private, ServerOnly = 1 ]
{
    s ..vitality = vitality
    s ..attact = attact
    s ..defense = defense
    Quit $$$OK
}

}

管理类

Class PHA.YX.Design.Memento.RoleStateCaretaker Extends %RegisteredObject
{

Property memento As RoleStateMemento [ Private ];

Method mementoGet() As RoleStateMemento [ ServerOnly = 1 ]
{
    Quit i%memento
}

Method mementoSet(Arg As RoleStateMemento) As %Status [ ServerOnly = 1 ]
{
    s i%memento = Arg
    Quit $$$OK
}

}

思考

记录听歌时的状态(歌曲名称,百分比,播放模式)。感兴趣的同学实现后可以发我一起参考下。

第十五章 Caché 设计模式 组合模式

定义

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

使用场景

需求中是体现部分与整体层次的结构时,可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑用组合模式了

优点

  • 组合模式可以以层次结构清楚的定义复杂对象。让高层次忽略层次差异。
  • 高层次可以使用统一的方法而不用担心它是枝干还是叶子
  • 组合模式可以形成复杂的树形结构,但对树形机构的控制却非常简单

缺点

  • 叶子类型不能控制

结果图

image

完整示例

抽象类

Class PHA.YX.Design.Composite.Company Extends %RegisteredObject
{

Property name As %String;

Method %OnNew(name As %String) As %Status [ Private, ServerOnly = 1 ]
{
    s ..name = name
    Quit $$$OK
}

Method Add(c As Company)
{
}

Method Remove(c As Company)
{
}

Method Display(depth As %Integer)
{
}

Method LineOfDuty()
{
}

}

具体类

树枝节点

Class PHA.YX.Design.Composite.ConcreteCompany Extends Company
{

Property children As list Of Company;

Method %OnNew(name As %String) As %Status [ Private, ServerOnly = 1 ]
{
    d ##super(name)
    Quit $$$OK
}

Method Add(c As Company)
{
    d ..children.Insert(c)
}

Method Remove(c As Company)
{
    d ..children.RemoveAt(c)
}

Method Display(depth As %Integer)
{
    w depth _ "级" _ ..name,!
    f i = 1 : 1 : ..children.Size d
    .d ..children.GetAt(i).Display(depth + 1)
}

Method LineOfDuty()
{
    f i = 1 : 1 : ..children.Size d
    .d ..children.GetAt(i).LineOfDuty()
}

}

树叶节点

Class PHA.YX.Design.Composite.HRDepartment Extends Company
{

Method %OnNew(name As %String) As %Status [ Private, ServerOnly = 1 ]
{
    d ##super(name)
    Quit $$$OK
}

Method Add(c As Company)
{
}

Method Remove(c As Company)
{
}

Method Display(depth As %Integer)
{
    w depth _ "级" _ ..name,!
}

Method LineOfDuty()
{
    w ..name _ "员工招聘培训管理",!
}

}

Class PHA.YX.Design.Composite.FinanceDepartment Extends Company
{

Method %OnNew(name As %String) As %Status [ Private, ServerOnly = 1 ]
{
    d ##super(name)
    Quit $$$OK
}

Method Add(c As Company)
{
}

Method Remove(c As Company)
{
}

Method Display(depth As %Integer)
{
    w depth _ "级" _ ..name,!
}

Method LineOfDuty()
{
    w ..name _ "公司财务收支管理",!
}

}

调用

/// d ##class(PHA.YX.Design.Program).Composite() 
ClassMethod Composite()
{
    #dim root as PHA.YX.Design.Composite.ConcreteCompany
    s root = ##class(PHA.YX.Design.Composite.ConcreteCompany).%New("北京总公司")
    d root.Add(##class(PHA.YX.Design.Composite.HRDepartment).%New("总公司人力资源部"))
    d root.Add(##class(PHA.YX.Design.Composite.HRDepartment).%New("总公司财务部"))

    #dim comp as PHA.YX.Design.Composite.ConcreteCompany
    s comp = ##class(PHA.YX.Design.Composite.ConcreteCompany).%New("天津分公司")
    d comp.Add(##class(PHA.YX.Design.Composite.HRDepartment).%New("天津公司人力资源部"))
    d comp.Add(##class(PHA.YX.Design.Composite.HRDepartment).%New("天津公司财务部"))
    d root.Add(comp)

    #dim comp1 as PHA.YX.Design.Composite.ConcreteCompany
    s comp1 = ##class(PHA.YX.Design.Composite.ConcreteCompany).%New("火星分公司")
    d comp1.Add(##class(PHA.YX.Design.Composite.HRDepartment).%New("火星公司人力资源部"))
    d comp1.Add(##class(PHA.YX.Design.Composite.HRDepartment).%New("火星公司财务部"))
    d comp.Add(comp1)

    #dim comp2 as PHA.YX.Design.Composite.ConcreteCompany
    s comp2 = ##class(PHA.YX.Design.Composite.ConcreteCompany).%New("三体分公司")
    d comp2.Add(##class(PHA.YX.Design.Composite.HRDepartment).%New("三体公司人力资源部"))
    d comp2.Add(##class(PHA.YX.Design.Composite.HRDepartment).%New("三体公司财务部"))
    d comp.Add(comp2)

    w "结构图",!
    d root.Display(1)
    w "职责",!
    d root.LineOfDuty()
}
DHC-APP>d ##class(PHA.YX.Design.Program).Composite()
结构图
1级北京总公司
2级总公司人力资源部
2级总公司财务部
2级天津分公司
3级天津公司人力资源部
3级天津公司财务部
3级火星分公司
4级火星公司人力资源部
4级火星公司财务部
3级三体分公司
4级三体公司人力资源部
4级三体公司财务部
职责
总公司人力资源部员工招聘培训管理
总公司财务部员工招聘培训管理
天津公司人力资源部员工招聘培训管理
天津公司财务部员工招聘培训管理
火星公司人力资源部员工招聘培训管理
火星公司财务部员工招聘培训管理
三体公司人力资源部员工招聘培训管理
三体公司财务部员工招聘培训管理

思考

画出本组产品线结构组织图。

第十六章 Caché 设计模式 迭代器模式

定义

提供一种方法顺序访问个聚合对象中各个元素,而又不暴露该对象的内部表示。

使用场景

  • 提供一个list的遍历方法。目的很明确,弱化遍历算法和容器之间的关系
  • 根据传入的list额外提供一个遍历方法
  • 当需要对聚集有多种方式遍历时,可以考虑用迭代器模式。

结构图

image

完整示例

定义迭代器接口

Class PHA.YX.Design.Iterator.Iterator Extends %RegisteredObject
{

Method HasNext() As %Boolean
{
}

Method Next()
{
}

}

实现迭代器接口

Class PHA.YX.Design.Iterator.IteratorImpl Extends Iterator
{

Property list As list Of %String;

Property cursor As %Integer [ InitialExpression = 0 ];

Method %OnNew(list) As %Status [ Private, ServerOnly = 1 ]
{
    s ..list = list
    Quit $$$OK
}

Method HasNext() As %Boolean
{
    q ..cursor '= ..list.Count()
}

Method Next()
{
    s obj = ""
    i ..HasNext() d
    .s ..cursor = ..cursor +1
    .s obj = ..list.GetAt(..cursor)
    q obj
}

}

定义容器接口

Class PHA.YX.Design.Iterator.Container Extends %RegisteredObject
{

Method add(obj)
{
}

Method remove(obj)
{
}

Method Iterator(obj) As Iterator
{
}

}

实现容器接口

Class PHA.YX.Design.Iterator.ContainerImpl Extends Container
{

Property list As List Of %String;

Method add(obj)
{
    d ..list.Insert(obj)
}

Method remove(obj)
{
    d ..list.RemoveAt(obj)
}

Method Iterator() As Iterator
{
    q ##class(IteratorImpl).%New(..list)
}

}

调用

/// d ##class(PHA.YX.Design.Program).Iterator() 
ClassMethod Iterator()
{
    #dim container as PHA.YX.Design.Iterator.ContainerImpl
    s container = ##class(PHA.YX.Design.Iterator.ContainerImpl).%New()
    d container.add("Java")
    d container.add("Cache")
    d container.add("Kotlin")
    d container.add("Flutter")
    d container.add("Python")
    d container.add("Swfit")
    d container.add("Objective-c")
    d container.add("C#")

    #dim iterator as PHA.YX.Design.Iterator.Iterator
    s iterator = container.Iterator()
    while (iterator.HasNext()){
        w iterator.Next(),!
    }
}

DHC-APP>d ##class(PHA.YX.Design.Program).Iterator()
Java
Cache
Kotlin
Flutter
Python
Swfit
Objective-c
C#

思考

创建一个Person类有Name属性,添加,然后用迭代器模式遍历。

第十七章 Caché 设计模式 单例模式

定义

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

我们让一个全局变量使得一个对象访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。

使用场景

  • 整个项目需要一个共享访问点或共享数据
  • 创一个对象需要耗费的资源过多,比如访问I/O或者数据库等资源
  • 工具类对象

结构图

image

完整示例

双重锁定

Class PHA.YX.Design.Singleton.Singleton Extends %RegisteredObject
{

Parameter instance As COSEXPRESSION = "##class(PHA.YX.Design.Singleton.Singleton).%New()";

Method %OnNew() As %Status [ Private, ServerOnly = 1 ]
{
    Quit $$$OK
}

ClassMethod GetInstance() As Singleton
{
    # dim %instance as PHA.YX.Design.Singleton.Singleton
    i '$d(%instance) || (%instance = "") d
    .l +^INSTANCE("INSTANCE"):5  d
    ..i '$d(%instance) || (%instance = "") d
    ...s %instance = ##class(PHA.YX.Design.Singleton.Singleton).%New()
    l -^INSTANCE("INSTANCE")
    q %instance
}

Method Call()
{
    w "实例方法",!
}

}

为什么里面还有判断一次?

因为当instanc为null并且同时有个线程调用GetInstance()方法时,他们都将可以通过第一重instance=null的判断,然后由于lock机制,这两个变成则只有一个进入,另外一个在外排队等待,必须要其中的一个进入并出来后另外一个才能进入。而此时如果没有了第二重的instance是否为null判断,则第一个线程创建了实例,而第二个线程还是可以继续再创建新的实例,这就没有达到单例的目的。

调用

/// d ##class(PHA.YX.Design.Program).Singleton() 
ClassMethod Singleton()
{
    #dim s1 as PHA.YX.Design.Singleton.Singleton
    s s1 = ##class(PHA.YX.Design.Singleton.Singleton).GetInstance()
    d s1.Call()
    #dim s2 as PHA.YX.Design.Singleton.Singleton
    s s2 = ##class(PHA.YX.Design.Singleton.Singleton).GetInstance() 
    d s2.Call()
    if (s1 = s2) d
    .w "想个对象是相同的实例"
}
DHC-APP>d ##class(PHA.YX.Design.Program).Singleton()
实例方法
实例方法
想个对象是相同的实例

第十八章 Caché 设计模式 桥接模式

定义

将抽象部分与它的实现部分分离,使它们都可以独立地变化。

优点

  • 两个维度,独立变化
  • 灵活的扩展,透明实现
  • 桥接模式将抽象部分和实现部分分离,解耦

结构图

image

描述

有苹果和三星手机,都可以实现通讯录和游戏。思考如果在加一个华为手机和听歌功能。

完整示例

手机软件抽象类

Class PHA.YX.Design.Bridge.HandsetSoft Extends %RegisteredObject
{

Method Run() [ Abstract ]
{
}

}

游戏,通讯录具体类

Class PHA.YX.Design.Bridge.HandsetGame Extends HandsetSoft
{

Method Run()
{
    w "运行手机游戏",!
}

}

Class PHA.YX.Design.Bridge.HandsetAddressList Extends HandsetSoft
{

Method Run()
{
    w "运行手机通讯录",!
}

}

抽象手机品牌类

Class PHA.YX.Design.Bridge.HandsetBrand Extends %RegisteredObject
{

Property soft As HandsetSoft;

Method SetHandsetSoft(soft As HandsetSoft)
{
    s ..soft = soft
}

Method Run() [ Abstract ]
{
}

}

手机品牌具体类

Class PHA.YX.Design.Bridge.HandsetBrandApple Extends HandsetBrand
{

Method Run()
{
    w "苹果手机"
    d ..soft.Run()
}

}

Class PHA.YX.Design.Bridge.HandsetBrandSamsung Extends HandsetBrand
{

Method Run()
{
    w "三星手机"
    d ..soft.Run()
}

}

调用

/// d ##class(PHA.YX.Design.Program).Bridge() 
ClassMethod Bridge()
{
    #dim brand as PHA.YX.Design.Bridge.HandsetBrand
    s brand = ##class(PHA.YX.Design.Bridge.HandsetBrandApple).%New()

    d brand.SetHandsetSoft(##class(PHA.YX.Design.Bridge.HandsetGame).%New())
    d brand.Run()

    d brand.SetHandsetSoft(##class(PHA.YX.Design.Bridge.HandsetAddressList).%New())
    d brand.Run()

    s brand = ##class(PHA.YX.Design.Bridge.HandsetBrandSamsung).%New()

    d brand.SetHandsetSoft(##class(PHA.YX.Design.Bridge.HandsetGame).%New())
    d brand.Run()

    d brand.SetHandsetSoft(##class(PHA.YX.Design.Bridge.HandsetAddressList).%New())
    d brand.Run()
}
DHC-APP>d ##class(PHA.YX.Design.Program).Bridge()
苹果手机运行手机游戏
苹果手机运行手机通讯录
三星手机运行手机游戏
三星手机运行手机通讯录

思考

有三种图形,正方形,圆形,三角形,分别上白色,红色,黑色。

第十九章 Caché 设计模式 命令模式

定义

命令模式将每个请求封装成一个对象,从而让用户使用不同的请求把客户端参数化;将请求进行排队或者记录请求日志,以及支持可撤销操作。

优点

  • 行为请求和行为实现弱耦合,易扩展,修改,维护
  • 把请求一个操作的对象与知道怎么执行一个操作的对象分割开

缺点

设计模式通病,大量衍生类的创建。

作用

  • 它较容易地设计一个命令队列
  • 在需要情况下,可以较容易地将命令计入日志
  • 允许接收请求的一方决定是否要否决请求
  • 可以容易地实现对请求的撤销和重做
  • 由于加进新的具体命令类不影响其他的类,因此增加新的具体命令类很容易。

结构图

image

描述

去烧烤店吃烧烤,点烤鸡翅和羊肉串给处方下命令。

完整示例

烧烤者

Class PHA.YX.Design.Command.Barbecuer Extends %RegisteredObject
{

Method BakeMutton()
{
    w "烤羊肉串!",!
}

Method BakeChickenWing()
{
    w "烤鸡翅!",!
}

}

抽象命令

Class PHA.YX.Design.Command.Command Extends %RegisteredObject
{

Property receiver As Barbecuer [ Private ];

Method %OnNew(receiver As Barbecuer) As %Status [ Private, ServerOnly = 1 ]
{
    s ..receiver = receiver
    Quit $$$OK
}

Method ExcuteCommand() [ Abstract ]
{
}

}

具体命令

Class PHA.YX.Design.Command.BakeMuttonCommand Extends Command
{

Method %OnNew(receiver As Barbecuer) As %Status [ Private, ServerOnly = 1 ]
{
    d ##super(receiver)
    Quit $$$OK
}

Method ExcuteCommand()
{
    d ..receiver.BakeMutton()
}

}

Class PHA.YX.Design.Command.BakeChickenWingCommand Extends Command
{

Method %OnNew(receiver As Barbecuer) As %Status [ Private, ServerOnly = 1 ]
{
    d ##super(receiver)
    Quit $$$OK
}

Method ExcuteCommand()
{
    d ..receiver.BakeChickenWing()
}

}

服务员

Class PHA.YX.Design.Command.Waiter Extends %RegisteredObject
{

Property orders As list Of Command [ Private ];

Method SetOrder(command As Command)
{
    d ..orders.Insert(command)
    w "增加订单" _ command.%ClassName() _ "时间" _ $zd($h,3) _ " " _ $zt($p($h, ",", "2"),1),!
}

Method CancelOrder(command As Command)
{
    d ..orders.RemoveAt(command)
    w "取消订单" _ command.%ClassName() _ "时间" _ $zd($h,3) _ " " _ $zt($p($h, ",", "2"),1),!
}

Method Notify()
{
    f i = 1 : 1 : ..orders.Count() d
    .d ..orders.GetAt(i).ExcuteCommand()
}

}

调用

/// d ##class(PHA.YX.Design.Program).Command() 
ClassMethod Command()
{
    #dim boy as PHA.YX.Design.Command.Barbecuer
    s boy = ##class(PHA.YX.Design.Command.Barbecuer).%New()
    s bakeMuttonCommand1 = ##class(PHA.YX.Design.Command.BakeMuttonCommand).%New(boy)
    s bakeMuttonCommand2 = ##class(PHA.YX.Design.Command.BakeMuttonCommand).%New(boy)
    s bakeChickenWingCommand1 = ##class(PHA.YX.Design.Command.BakeChickenWingCommand).%New(boy)

    #dim girl as PHA.YX.Design.Command.Waiter
    s girl = ##class(PHA.YX.Design.Command.Waiter).%New()
    d girl.SetOrder(bakeMuttonCommand1)
    d girl.SetOrder(bakeMuttonCommand1)
    d girl.CancelOrder(bakeMuttonCommand1)
    d girl.SetOrder(bakeChickenWingCommand1)

    d girl.Notify()
}
DHC-APP>d ##class(PHA.YX.Design.Program).Command()
增加订单BakeMuttonCommand时间2020-04-07 09:43:56
增加订单BakeMuttonCommand时间2020-04-07 09:43:56
取消订单BakeMuttonCommand时间2020-04-07 09:43:56
增加订单BakeChickenWingCommand时间2020-04-07 09:43:56
烤羊肉串!
烤羊肉串!
烤鸡翅!

思考

电视机有 声音增大,减小,节目增加,减少,四个命令。客户端调用这四个命令。感兴趣的同学写完可以发我。

第二十章 Caché 设计模式 职责链模式

定义

使多个对象都有机会处理请求,从而避免请求发送者与接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,知道有一个对象处理它为止。

优点

  • 请求者和处理者关系解耦,提高代码灵活性
  • 当客户提交一个请求时,请求是沿着链传递直至有一个ConcreteHandler对象负责处理它
  • 随时地增加或修改处理一个请求的结构。增强了对象指派职责的灵活性
  • 接收者和发送者都没有对象的明确信息,且链中的对象自己也并不知道链的结构,结果是职责链可简化对象的相互连接,它们仅需保持一个指向其后继承者的引用,而不需保持它所有的候选接受者的引用。

缺点

  • 对处理着遍历,弱处理者太多,会影响性能,特别在递归处理中尤其需要注意。

结构图

image

描述

申请人,申请请假或加薪,随着申请权限不同,扭转不同管理者。

完整示例

请求类

Class PHA.YX.Design.Chain.Request Extends %RegisteredObject
{

Property requestType As %String;

Method requestTypeGet() As %String [ ServerOnly = 1 ]
{

    Quit i%requestType
}

Method requestTypeSet(Arg As %String) As %Status [ ServerOnly = 1 ]
{
    s i%requestType = Arg
    Quit $$$OK
}

Property requestContent As %String;

Method requestContentGet() As %String [ ServerOnly = 1 ]
{
    Quit i%requestContent
}

Method requestContentSet(Arg As %String) As %Status [ ServerOnly = 1 ]
{
    s i%requestContent = Arg
    Quit $$$OK
}

Property number As %String;

Method numberGet() As %String [ ServerOnly = 1 ]
{
    Quit i%number
}

Method numberSet(Arg As %String) As %Status [ ServerOnly = 1 ]
{
    s i%number = Arg
    Quit $$$OK
}

}

抽象责任类

Class PHA.YX.Design.Chain.Manager Extends %RegisteredObject
{

Property name As %String;

Property superior As Manager;

Method %OnNew(name As %String) As %Status [ Private, ServerOnly = 1 ]
{
    s ..name = name
    Quit $$$OK
}

Method SetSuperior(superior As Manager)
{
    s ..superior = superior
}

Method RequertApplications(request As Request) [ Abstract ]
{
}

}

实现责任类

Class PHA.YX.Design.Chain.CommonManager Extends Manager
{

Method %OnNew(name As %String) As %Status [ Private, ServerOnly = 1 ]
{
    d ##super(name)
    Quit $$$OK
}

Method RequertApplications(request As Request)
{
    i (request.requestTypeGet() = "请假") && (request.numberGet() <= 2) d
    .w ..name _ ":" _ request.requestContentGet() _ "数量" _ request.numberGet() _ "被批准",!
    e  d
    .i ..superior '= "" d
    ..d ..superior.RequertApplications(request)
}

}

Class PHA.YX.Design.Chain.MajorManager Extends Manager
{

Method %OnNew(name As %String) As %Status [ Private, ServerOnly = 1 ]
{
    d ##super(name)
    Quit $$$OK
}

Method RequertApplications(request As Request)
{
    i (request.requestTypeGet() = "请假") && (request.numberGet() <= 5) d
    .w ..name _ ":" _ request.requestContentGet() _ "数量" _ request.numberGet() _ "被批准",!
    e  d
    .i ..superior '= "" d
    ..d ..superior.RequertApplications(request)
}

}

Class PHA.YX.Design.Chain.GeneralManager Extends Manager
{

Method %OnNew(name As %String) As %Status [ Private, ServerOnly = 1 ]
{
    d ##super(name)
    Quit $$$OK
}

Method RequertApplications(request As Request)
{
    i (request.requestTypeGet() = "请假") d
    .w ..name _ ":" _ request.requestContentGet() _ " 数量" _ request.numberGet() _ "被批准",!
    e  i (request.requestTypeGet() = "加薪") && (request.numberGet() <=500) d
    .w ..name _ ":" _ request.requestContentGet() _ " 数量" _ request.numberGet() _ "被批准",!
    e  i (request.requestTypeGet() = "加薪") && (request.numberGet() >500) d
    .w ..name _ ":" _ request.requestContentGet() _ " 数量" _ request.numberGet() _ "再说吧",!
}

}

调用

/// d ##class(PHA.YX.Design.Program).Chain() 
ClassMethod Chain()
{
    s zz = ##class(PHA.YX.Design.Chain.CommonManager).%New("周总")
    s sz = ##class(PHA.YX.Design.Chain.MajorManager).%New("苏总")
    s hz = ##class(PHA.YX.Design.Chain.GeneralManager).%New("韩总")

    d zz.SetSuperior(sz)
    d sz.SetSuperior(hz)

    s request = ##class(PHA.YX.Design.Chain.Request).%New()
    d request.numberSet(1)
    d request.requestContentSet("丁竑莹请假")
    d request.requestTypeSet("请假")
    d zz.RequertApplications(request)

    s request1 = ##class(PHA.YX.Design.Chain.Request).%New()
    d request1.numberSet(4)
    d request1.requestContentSet("小马哥请假")
    d request1.requestTypeSet("请假")
    d zz.RequertApplications(request1)

    s request2 = ##class(PHA.YX.Design.Chain.Request).%New()
    d request2.numberSet(500)
    d request2.requestContentSet("姚鑫请求加薪")
    d request2.requestTypeSet("加薪")
    d zz.RequertApplications(request2)

    s request3 = ##class(PHA.YX.Design.Chain.Request).%New()
    d request3.numberSet(1000)
    d request3.requestContentSet("姚鑫请求加薪")
    d request3.requestTypeSet("加薪")
    d zz.RequertApplications(request3)
}
DHC-APP>d ##class(PHA.YX.Design.Program).Chain()
周总:丁竑莹请假数量1被批准
苏总:小马哥请假数量4被批准
韩总:姚鑫请求加薪 数量500被批准
韩总:姚鑫请求加薪 数量1000再说吧

思考

英雄联盟,黑铁,青铜,白银,黄金,铂金,钻石,大师,王者。请求者为对应级别才可以匹配,否则扭转。

第二十一章 Caché 设计模式 中介者模式

定义

用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显示的相互引用,从而使其耦合松散,而且可以独立的改变他们之前的交互。

优点

  • 中介者模式就是把网状复杂结构优化为一对多结构。起到协调作用
  • MVP架构里面P层其实就是一个中介者,负责协调V和M

使用场景

中介者模式很容易在系统中应用,也很容易在系统中误用,当胸出现了多对多交互复杂的对象群时,不要急于使用中介者模式,而要反思你的系统在设计上是不是合理。

中介者模式一般应用与一组对象以定义良好但是复杂的方式进行通信的场合。以及想定制一个分布在多个类中的行为,而又不想生成太多的子类的场合。

结构图

image

描述

安理会做中介,给美国和伊朗传话。

完整示例

抽象类

Class PHA.YX.Design.Mediator.Country Extends %RegisteredObject
{

Property mediator As UnitedNations;

Method %OnNew(mediator As UnitedNations) As %Status [ Private, ServerOnly = 1 ]
{
    s ..mediator = mediator
    Quit $$$OK
}

}

具体类

Class PHA.YX.Design.Mediator.USA Extends Country
{

Method %OnNew(mediator As UnitedNations) As %Status [ Private, ServerOnly = 1 ]
{
    d ##super(mediator)
    Quit $$$OK
}

Method Declare(message As %String)
{
    d ..mediator.Declare(message,$this)
}

Method GetMessage(message As %String)
{
    w "美国获得对方信息" _ message,!
}

}

Class PHA.YX.Design.Mediator.Iraq Extends Country
{

Method %OnNew(mediator As UnitedNations) As %Status [ Private, ServerOnly = 1 ]
{
    d ##super(mediator)
    Quit $$$OK
}

Method Declare(message As %String)
{
    d ..mediator.Declare(message,$this)
}

Method GetMessage(message As %String)
{
    w "伊拉克获得对方信息" _ message,!
}

}

抽象中介类

Class PHA.YX.Design.Mediator.UnitedNations Extends %RegisteredObject
{

Method Declare(message As %String, colleague As Country) [ Abstract ]
{
}

}

具体中介类

Class PHA.YX.Design.Mediator.UnitedNationSecurityCouncil Extends UnitedNations
{

Property iraq As Iraq;

Method iraqGet() As Iraq [ ServerOnly = 1 ]
{
    Quit i%iraq
}

Method iraqSet(Arg As Iraq) As %Status [ ServerOnly = 1 ]
{
    s i%iraq = Arg
    Quit $$$OK
}

Property usa As USA;

Method usaGet() As USA [ ServerOnly = 1 ]
{
    Quit i%usa
}

Method usaSet(Arg As USA) As %Status [ ServerOnly = 1 ]
{
    s i%usa = Arg
    Quit $$$OK
}

Method Declare(message As %String, colleague As Country)
{
    i colleague = ..usa d
    .d ..iraq.GetMessage(message)
    e  d
    .d ..usa.GetMessage(message)
}

}

调用

/// d ##class(PHA.YX.Design.Program).Mediator() 
ClassMethod Mediator()
{
    s UNSC = ##class(PHA.YX.Design.Mediator.UnitedNationSecurityCouncil).%New()
    s usa = ##class(PHA.YX.Design.Mediator.USA).%New(UNSC)
    s iraq = ##class(PHA.YX.Design.Mediator.Iraq).%New(UNSC)

    d UNSC.iraqSet(iraq)
    d UNSC.usaSet(usa)

    d usa.Declare("不准研发而武器,否则要放战争!")
    d iraq.Declare("我们没有核武器,也不怕侵略.")
}

DHC-APP>d ##class(PHA.YX.Design.Program).Mediator()
伊拉克获得对方信息不准研发而武器,否则要放战争!
美国获得对方信息我们没有核武器,也不怕侵略.

思考

尝试画出View,Presenter,Model 三者通过Presenter来通信,View与Model互相不通信。

第二十二章 Caché 设计模式 享元模式

定义

运用共享技术有效地支持大量细粒度的对象。

优点

享元模式可以避免大量非常相似类的开销,在程序设计中,有时需要生成大量细粒度的类实例来表示数据。如果能发现这些实例除了几个参数外基本上相同的,有时就能够大幅度地减少需要实例化的类的数量。如果把这些参数移到类实例外面,在方法调用时传递他们进来,就可以通过共享大幅度减少单个实例的数目。

使用场景

  • 系统中存在大量的相似对象
  • 需要缓冲池场景
  • 如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的储存开销时就硬挨考虑使用

结构图

image

描述

做六个网站,三个产品展示,三个博客网站。

完整示例

实体类

Class PHA.YX.Design.Flyweight.User Extends %RegisteredObject
{

Property name As %String;

Method nameGet() As %String [ ServerOnly = 1 ]
{
    Quit i%name
}

Method nameSet(Arg As %String) As %Status [ ServerOnly = 1 ]
{
    s i%name = Arg
    Quit $$$OK
}

Method %OnNew(name As %String) As %Status [ Private, ServerOnly = 1 ]
{
    s ..name = name
    Quit $$$OK
}

}

抽象享元角色

Class PHA.YX.Design.Flyweight.WebSite Extends %RegisteredObject
{

Method Use(user As User) [ Abstract ]
{
}

}

实现享元角色

Class PHA.YX.Design.Flyweight.ConcreteWebSite Extends WebSite
{

Property name As %String;

Method %OnNew(name As %String) As %Status [ Private, ServerOnly = 1 ]
{
    s ..name = name
    Quit $$$OK
}

Method Use(user As User)
{
    w "网站分类" _ ..name _ "用户," _ user.nameGet(),!
}

}

享元工厂

Class PHA.YX.Design.Flyweight.WebSiteFactory Extends %RegisteredObject
{

Property flyweights As %ArrayOfDataTypes;

Method GetWebSiteCategory(key As %String) As WebSite
{
    i '$d(..flyweights.Data(key)) d
    .d ..flyweights.SetAt(##class(ConcreteWebSite).%New(key),key)
    q ..flyweights.GetAt(key)
}

Method GetWebSiteCount() As %Integer
{
    q ..flyweights.Count()
}

}

调用

/// d ##class(PHA.YX.Design.Program).Flyweight() 
ClassMethod Flyweight()
{
    s factory = ##class(PHA.YX.Design.Flyweight.WebSiteFactory).%New()
    #dim fx as PHA.YX.Design.Flyweight.ConcreteWebSite
    s fx = factory.GetWebSiteCategory("产品展示")
    d fx.Use(##class(PHA.YX.Design.Flyweight.User).%New("姚鑫"))

    s fy = factory.GetWebSiteCategory("产品展示")
    d fy.Use(##class(PHA.YX.Design.Flyweight.User).%New("小马哥"))

    s fz = factory.GetWebSiteCategory("产品展示")
    d fz.Use(##class(PHA.YX.Design.Flyweight.User).%New("宝哥"))

    s fl = factory.GetWebSiteCategory("博客")
    d fl.Use(##class(PHA.YX.Design.Flyweight.User).%New("杨过"))

    s fm = factory.GetWebSiteCategory("博客")
    d fm.Use(##class(PHA.YX.Design.Flyweight.User).%New("小龙女"))

    s fn = factory.GetWebSiteCategory("博客")
    d fn.Use(##class(PHA.YX.Design.Flyweight.User).%New("殷素素"))

    w "得到网站分类总数为" _ factory.GetWebSiteCount(),!
}
DHC-APP>d ##class(PHA.YX.Design.Program).Flyweight()
网站分类产品展示用户,姚鑫
网站分类产品展示用户,小马哥
网站分类产品展示用户,宝哥
网站分类博客用户,杨过
网站分类博客用户,小龙女
网站分类博客用户,殷素素
得到网站分类总数为2

思考

淘宝商品 iphone7,iphone8 32G 64G 128G,利用享元模式如何实现。

第二十三章 Caché 设计模式 解释器模式

定义

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

使用场景

  • 如果一种特性类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,爱解释器通过解释这些句子来解决该问题。
  • 有一个简单的语法规则,比如编码转换.翻译

  • 正则表达式,浏览器。

优点

扩展性强,若要新增乘,除,添加相应的非终结表达式,修改计算逻辑即可。

缺点

  • 需要建大量的类,因为每一种语法都要建一个非终结符的类。
  • 解释的时候采用递归调用方法,导致有时候函数的深度会很深,影响效率。

结构图

image

描述

用abc来代替123数字。

完整示例

抽象解释器

Class PHA.YX.Design.Interpreter.Expression Extends %RegisteredObject
{

Method Interpreter(context As Context) As %Integer [ Abstract ]
{
    q 0
}

}

上下文

Class PHA.YX.Design.Interpreter.Context Extends %RegisteredObject
{

Property map As %ArrayOfDataTypes;

Method Add(s As Expression, value As %Integer)
{
    d ..map.SetAt(value, s)
}

Method Lookup(s As Expression)
{
    q ..map.GetAt(s)
}

}

终结符表达式

Class PHA.YX.Design.Interpreter.TerminalExpression Extends Expression
{

Property variable As %String;

Method %OnNew(variable As %String) As %Status [ Private, ServerOnly = 1 ]
{
    s ..variable = variable
    Quit $$$OK
}

Method Interpreter(context As Context) As %Integer
{
    q context.Lookup($this)
}

}

非终结符表达式

Class PHA.YX.Design.Interpreter.NonTerminalExpression Extends Expression
{

Property e1 As Expression;

Property e2 As Expression;

Method %OnNew(e1 As Expression, e2 As Expression) As %Status [ Private, ServerOnly = 1 ]
{
    s ..e1 = e1
    s ..e2 = e2
    Quit $$$OK
}

}

Class PHA.YX.Design.Interpreter.MinusOperation Extends NonTerminalExpression
{

Method %OnNew(e1 As Expression, e2 As Expression) As %Status [ Private, ServerOnly = 1 ]
{
    d ##super(e1, e2)
    Quit $$$OK
}

Method Interpreter(context As Context) As %Integer
{

    q ..e1.Interpreter(context) - ..e2.Interpreter(context)
}

}

Class PHA.YX.Design.Interpreter.PlusOperation Extends NonTerminalExpression
{

Method %OnNew(e1 As Expression, e2 As Expression) As %Status [ Private, ServerOnly = 1 ]
{
    d ##super(e1, e2)
    Quit $$$OK
}

Method Interpreter(context As Context) As %Integer
{
    q ..e1.Interpreter(context) + ..e2.Interpreter(context)
}

}

调用

/// d ##class(PHA.YX.Design.Program).Interpreter() 
ClassMethod Interpreter()
{
    s context = ##class(PHA.YX.Design.Interpreter.Context).%New()
    s a = ##class(PHA.YX.Design.Interpreter.TerminalExpression).%New("a")
    s b = ##class(PHA.YX.Design.Interpreter.TerminalExpression).%New("b")
    s c = ##class(PHA.YX.Design.Interpreter.TerminalExpression).%New("c")
    d context.Add(a, 4)
    d context.Add(b, 8)
    d context.Add(c, 6)
    s min= ##class(PHA.YX.Design.Interpreter.MinusOperation).%New(a,b).Interpreter(context)

    w min,!

    w ##class(PHA.YX.Design.Interpreter.MinusOperation).%New(##class(PHA.YX.Design.Interpreter.PlusOperation).%New(a, b), c).Interpreter(context),!
}

DHC-APP> d ##class(PHA.YX.Design.Program).Interpreter()
-4
6

思考

把数字解释解释成对应英文。

第二十四章 Caché 设计模式 访问者模式

定义

表示一个作用域某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用与这些元素的新操作。

使用场景

  • 被访问者不变
  • 根据访问者和被访问者的不同,两两对应达到不同的目的。
  • 遍历被访问者实现“访问”。
  • 访问者模式适用于数据结构相对稳定的系统,他把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化。

目的

访问者模式目的是要把处理从数据结构分离出来。很多系统可以按照算法和数据结构分开,如果这样的系统有比较稳定的数据结构,又易于变化的算法的话使用访问者模式就是比较合适的。因为访问者模式使得算法操作的增加变得容易。

优点

  • 各角色职责分离,符合单一原则
  • 扩展十分方便,灵活
  • 数据结构和数据结构上的操作解耦

缺点

  • 被访问者对访问者公布了细节,违反迪米特原则
  • 被访问者要改动的时候,修改十分麻烦
  • 访问者和被访者为了达到不同的行为目的的时候,为了区分依赖了类的不同,没有依赖抽象

结构图

image

描述

用访问者模式来改写。

/// d ##class(PHA.YX.Design.Program).VisitorMethod() 
ClassMethod VisitorMethod()
{
    w "男人成功时,背后多半有个伟大的女人",!
    w "女人成功时,背后大多有一个不成功的男人",!
    w "男人失败时,闷头喝酒,谁也不用劝",!
    w "女人失败时,眼泪汪汪,谁也劝不了",!
    w "男人恋爱时,凡事不懂也要装懂",!
    w "女人恋爱时,遇事懂也装作不懂",!
}

完整示例

状态抽象类

Class PHA.YX.Design.Visitor.Action Extends %RegisteredObject [ Abstract ]
{

Method GetManConclusion(concreteElementA As Man) [ Abstract ]
{
}

Method GetWomanConclusion(concreteElementB As Woman) [ Abstract ]
{
}

}

人抽象类

Class PHA.YX.Design.Visitor.Person Extends %RegisteredObject [ Abstract ]
{

Method Accept(visitor As Action) [ Abstract ]
{
}

}

具体状态类

Class PHA.YX.Design.Visitor.Success Extends Action
{

Method GetManConclusion(concreteElementA As Man)
{
    w concreteElementA.%ClassName(0) _ " " _$this.%ClassName(0) _ " 背后多半有一个伟大的女人",!
}

Method GetWomanConclusion(concreteElementB As Woman)
{
    w concreteElementB.%ClassName(0) _ " " _$this.%ClassName(0) _ " 背后多半有一个不成功的男人",!
}

}

Class PHA.YX.Design.Visitor.Failing Extends Action
{

Method GetManConclusion(concreteElementA As Man)
{
    w concreteElementA.%ClassName(0) _ " " _$this.%ClassName(0) _ " 闷头喝酒,谁也不用劝",!
}

Method GetWomanConclusion(concreteElementB As Woman)
{
    w concreteElementB.%ClassName(0) _ " " _$this.%ClassName(0) _ " 眼泪汪汪,谁也劝不了",!
}

}

Class PHA.YX.Design.Visitor.Amativeness Extends Action
{

Method GetManConclusion(concreteElementA As Man)
{
    w concreteElementA.%ClassName(0) _ " " _ $this.%ClassName(0) _ " 凡事不懂也要装懂",!
}

Method GetWomanConclusion(concreteElementB As Woman)
{
    w concreteElementB.%ClassName(0) _ " " _$this.%ClassName(0) _ " 遇事懂也装作不懂",!
}

}

Class PHA.YX.Design.Visitor.Marriage Extends Action
{

Method GetManConclusion(concreteElementA As Man)
{
    w concreteElementA.%ClassName(0) _ " " _ $this.%ClassName(0) _ " 感概道:恋爱游戏终结时,进入爱情的坟墓",!
}

Method GetWomanConclusion(concreteElementB As Woman)
{
    w concreteElementB.%ClassName(0) _ " " _$this.%ClassName(0) _ " 爱情长跑路漫漫,婚宴保险保平安",!
}

}

具体人类

Class PHA.YX.Design.Visitor.Man Extends Person
{

Method Accept(visitor As Action)
{
    d visitor.GetManConclusion($this)
}

}

Class PHA.YX.Design.Visitor.Woman Extends Person
{

Method Accept(visitor As Action)
{
    d visitor.GetWomanConclusion($this)
}

}

对象结构类

Class PHA.YX.Design.Visitor.ObjectStructure Extends %RegisteredObject
{

Property elements As list Of Person;

Method Attach(element As Person)
{
    d ..elements.Insert(element)
}

Method Remove(element As Person)
{
    d ..elements.RemoveAt(element)
}

Method Display(visitor As Action)
{
    f i = 1 : 1 : ..elements.Count() d
    .d ..elements.GetAt(i).Accept(visitor)
}

}

调用

/// d ##class(PHA.YX.Design.Program).Visitor() 
ClassMethod Visitor()
{
    s o = ##class(PHA.YX.Design.Visitor.ObjectStructure).%New()
    d o.Attach(##class(PHA.YX.Design.Visitor.Man).%New())
    d o.Attach(##class(PHA.YX.Design.Visitor.Woman).%New())

    s v1 =  ##class(PHA.YX.Design.Visitor.Success).%New()
    d o.Display(v1)

    s v2 =  ##class(PHA.YX.Design.Visitor.Failing).%New()
    d o.Display(v2)

    s v3 =  ##class(PHA.YX.Design.Visitor.Amativeness).%New()
    d o.Display(v3)
}
DHC-APP>d ##class(PHA.YX.Design.Program).Visitor()
Man Success 背后多半有一个伟大的女人
Woman Success 背后多半有一个不成功的男人
Man Failing 闷头喝酒,谁也不用劝
Woman Failing 眼泪汪汪,谁也劝不了
Man Amativeness 凡事不懂也要装懂
Woman Amativeness 遇事懂也装作不懂

思考

语文老师,数学老师,体育老师,分别访问 艺术生和体育生的成绩。

讨论 (2)1
登录或注册以继续