diff --git a/src/dp/com/company/adapter/README.md b/src/dp/com/company/adapter/README.md index 800f126..140887a 100644 --- a/src/dp/com/company/adapter/README.md +++ b/src/dp/com/company/adapter/README.md @@ -1,27 +1,27 @@ -#适配器模式(Adapter Pattern) -定义:Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.(将一个类的接口变换成客户端所期待的另一种接口,从而是原本因接口不匹配而无法在一起工作的两个类能够在一起工作。) - - -适配器模式通用类图如图所示。 -![Alt text](adapter.jpg "适配器模式类图") - - - 我们先来看看适配器模式的三个角色: - -- Target目标角色:该角色定义把其它类转换为何种接口,也就是我们的期望接口。 -- Adaptee源角色:你想把谁转换成目标角色。 -- Adapter适配器角色:适配器模式的核心角色,其它两个角色都是已经存在的角色,而适配器角色是需要新建立的,它的职责非常简单,把源角色转换成目标角色,怎么转换?通过继承或类关联的方式。 - - -#适配器模式的应用 -##适配器模式的优点 - * 适配器模式可以让两个没有任何关系得类在一起运行,只要适配器这个角色能够搞定他就成。 - * 增加了类的透明性。我们访问的Target目标角色,但是具体的实现都委托给了源角色,而这些对高层次模块时透明的,也是它不需要关心的。 - * 提高了类的复用度。 - * 灵活性非常好 - -##适配器模式的使用场景 - 适配器应用的场景只要记住一点就足够了:你有动机修改了一个已经投产中的接口时,适配器模式可能是最适合你的模式。 - -##适配器模式的注意事项 +# 适配器模式(Adapter Pattern) +定义:Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.(将一个类的接口变换成客户端所期待的另一种接口,从而是原本因接口不匹配而无法在一起工作的两个类能够在一起工作。) + + +适配器模式通用类图如图所示。 +![Alt text](adapter.jpg "适配器模式类图") + + + 我们先来看看适配器模式的三个角色: + +- Target目标角色:该角色定义把其它类转换为何种接口,也就是我们的期望接口。 +- Adaptee源角色:你想把谁转换成目标角色。 +- Adapter适配器角色:适配器模式的核心角色,其它两个角色都是已经存在的角色,而适配器角色是需要新建立的,它的职责非常简单,把源角色转换成目标角色,怎么转换?通过继承或类关联的方式。 + + +# 适配器模式的应用 +### 1.适配器模式的优点 + * 适配器模式可以让两个没有任何关系得类在一起运行,只要适配器这个角色能够搞定他就成。 + * 增加了类的透明性。我们访问的Target目标角色,但是具体的实现都委托给了源角色,而这些对高层次模块时透明的,也是它不需要关心的。 + * 提高了类的复用度。 + * 灵活性非常好 + +### 2.适配器模式的使用场景 + 适配器应用的场景只要记住一点就足够了:你有动机修改了一个已经投产中的接口时,适配器模式可能是最适合你的模式。 + +### 3.适配器模式的注意事项 适配器模式最好在详细设计不要考虑它,它不是为了解决还处在开发阶段的问题,而是解决正在服役的项目问题。 \ No newline at end of file diff --git a/src/dp/com/company/bridge/README.md b/src/dp/com/company/bridge/README.md index 9583ee6..f6458f4 100644 --- a/src/dp/com/company/bridge/README.md +++ b/src/dp/com/company/bridge/README.md @@ -1,30 +1,30 @@ -#桥梁模式(Bridge Pattern) -定义:Decouple an abstraction from its implementation so that the two can vary independently.(将抽象和实现解耦,使得两者可以独立地变化。) - - 桥梁模式的通用类图如图所示。 -![Alt text](bridge.jpg "桥梁模式类图") - - -- Abstraction抽象化角色:它的主要职责是定义出该角色的行为,同时保存一个对实现化角色的引用,该角色一般是抽象类。 -- Implementor实现化角色:它是接口或者抽象类,定义角色必须的行为和属性。 -- RefinedAbstraction修正抽象化角色:它引用实现化角色对抽象化角色进行修正。 -- ConcreteImplementor具体实现化角色:它实现接口和抽象类定义的方法和属性。 - - -桥梁模式的几个名词比较拗口,大家只要记住一句话就成:抽象角色引用实现角色,或者说抽象角色的部分实现是由实现角色完成的。 - - -#桥梁模式的应用 -##1.桥梁模式的优点 - * 抽象和实现分离:它完全是为了解决集成的缺点而提出的设计模式。在该模式下,实现可以不受抽象的约束,不用再绑定在一个固定的抽象层次上。 - * 优秀的扩充能力。 - * 实现细节对客户透明。客户不用关心细节的实现,它已经由抽象层通过聚合关系完成了封装。 - - -##2.桥梁模式的使用场景 - * 不希望或不适合使用集成的场景:例如继承层次过渡、无法更细化设计颗粒等场景,需要考虑使用桥梁模式。 - * 接口或抽象类不稳定的场景:明知道接口不稳定还想通过实现或继承来实现业务需求,那是得不偿失的,也是比较失败得作法。 - * 重用性要求较高的场景:设计的颗粒度越细,则被重用的可能性就越大,而采用继承则受父类的限制,不可能出现太细的颗粒度。 - -##3.桥梁模式的注意事项 +# 桥梁模式(Bridge Pattern) +定义:Decouple an abstraction from its implementation so that the two can vary independently.(将抽象和实现解耦,使得两者可以独立地变化。) + + 桥梁模式的通用类图如图所示。 +![Alt text](bridge.jpg "桥梁模式类图") + + +- Abstraction抽象化角色:它的主要职责是定义出该角色的行为,同时保存一个对实现化角色的引用,该角色一般是抽象类。 +- Implementor实现化角色:它是接口或者抽象类,定义角色必须的行为和属性。 +- RefinedAbstraction修正抽象化角色:它引用实现化角色对抽象化角色进行修正。 +- ConcreteImplementor具体实现化角色:它实现接口和抽象类定义的方法和属性。 + + +桥梁模式的几个名词比较拗口,大家只要记住一句话就成:抽象角色引用实现角色,或者说抽象角色的部分实现是由实现角色完成的。 + + +# 桥梁模式的应用 +### 1.桥梁模式的优点 + * 抽象和实现分离:它完全是为了解决集成的缺点而提出的设计模式。在该模式下,实现可以不受抽象的约束,不用再绑定在一个固定的抽象层次上。 + * 优秀的扩充能力。 + * 实现细节对客户透明。客户不用关心细节的实现,它已经由抽象层通过聚合关系完成了封装。 + + +### 2.桥梁模式的使用场景 + * 不希望或不适合使用集成的场景:例如继承层次过渡、无法更细化设计颗粒等场景,需要考虑使用桥梁模式。 + * 接口或抽象类不稳定的场景:明知道接口不稳定还想通过实现或继承来实现业务需求,那是得不偿失的,也是比较失败得作法。 + * 重用性要求较高的场景:设计的颗粒度越细,则被重用的可能性就越大,而采用继承则受父类的限制,不可能出现太细的颗粒度。 + +### 3.桥梁模式的注意事项 桥梁模式是非常简单的,使用该模式时注意考虑如何拆分抽象和实现,并不是一涉及继承就要考虑使用该模式,那还要继承干什么呢?桥梁模式的意图还是对变化的封装,尽量把可能变化的因素封装到最细、最小的逻辑单元中,避免风险扩散。因此读者在进行系统设计时,发现类的继承有N层时,可以考虑使用桥梁模式。 \ No newline at end of file diff --git a/src/dp/com/company/builder/README.md b/src/dp/com/company/builder/README.md index 2e7d351..066afb5 100644 --- a/src/dp/com/company/builder/README.md +++ b/src/dp/com/company/builder/README.md @@ -1,36 +1,37 @@ -#建造者模式(Builder Pattern) -定义:Separate the construction of a complex object from its representation so that the same construction process can create different representations. 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 - -建造者模式的通用类图如下图: -![Alt text](builder.jpg "建造者模式类图") - -在建造者模式中,有如下四个角色: - -- Product产品类:通常是实现了模板方法模式,也就是有模板方法和基本方法,这个参考上一章节的模板方法模式。在例子中,BenzModel和BMWModel就属于产品类。 -- Builder抽象建造者:规范产品的组建,一般是由子类实现。在例子中,CarBuilder属于抽象建造者。 -- ConcreteBuilder具体建造者:实现抽象类定义的所有方法,并且返回一个组件好的对象。在例子中,BenzBuilder和BMWBuilder就属于具体建造者。 -- Director导演:负责安排已有模块的顺序,然后告诉Builder开始建造,在上面的例子中就是我们的老大,牛叉公司找到老大,说我要这个,这个,那个类型的车辆模型,然后老大就把命令传递给我,我和我的团队就开始拼命的建造,于是一个项目建设完毕了。 - -#建造者模式的应用 -##建造者模式的优点 - * 封装性。使用建造者模式可以使客户端不必知道产品内部组成的细节,如例子中我们就不需要关心每一个具体的模型内部是如何实现的,产生的对象类型就是CarModel。 - * 建造者独立,容易扩展。 - * 便于控制细节风险。由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。 - -##建造者模式的使用场景 - * 相同的方法,不同的执行顺序,产生不同的事件结果时,可以采用建造者模式。 - * 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时,则可以使用该模式。 - * 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式是非常合适。 - * 在对象创建过程中会使用到系统中的一些其它对象,这些对象在产品对象的创建过程中不易得到时,也可以采用建造者模式封装该对象的创建过程。该种场景,只能是一个补偿方法,因为一个对象不容易获得,而在设计阶段竟然没有发觉,而要通过创建者模式柔化创建过程,本身已经违反设计最初目标。 - -##建造者模式的注意事项 -建造者模式关注的是的零件类型和装配工艺(顺序),这是它与工厂方法模式最大不同的地方,虽然同为创建类模式,但是注重点不同。 - -#建造者模式的扩展 -已经不用扩展了,因为我们在汽车模型制造的例子中已经对建造者模式进行了扩展,引入了模板方法模式,可能大家会比较疑惑,为什么在其他介绍设计模式的书籍上创建者模式并不是这样说的,读者请注意,建造者模式中还有一个角色没有说明,就是零件,建造者怎么去建造一个对象?是零件的组装,组装顺序不同对象效能也不同,这才是建造者模式要表达的核心意义,而怎么才能更好的达到这种效果呢?引入模板方法模式是一个非常简单而有效的办法。 -大家看到这里估计就开始犯嘀咕了,这个建造者模式和工厂模式非常相似呀,Yes,是的,是非常相似,但是记住一点你就可以游刃有余的使用了:建造者模式最主要功能是基本方法的调用顺序安排,也就是这些基本方法已经实现了,通俗的说就是零件的装配,顺序不同产生的对象也不同;而工厂方法则重点是创建,创建零件时它的主要职责,你要什么对象我创造一个对象出来,组装顺序则不是他关心的。 - -#最佳实践 -再次说明,在使用建造者模式的时候考虑一下模板方法模式,别孤立的思考一个模式,僵化的套用一个模式会让受害无穷! - +# 建造者模式(Builder Pattern) +定义:Separate the construction of a complex object from its representation so that the same construction process can create different representations. 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 + +建造者模式的通用类图如下图: +![Alt text](builder.jpg "建造者模式类图") + +在建造者模式中,有如下四个角色: + +- Product产品类:通常是实现了模板方法模式,也就是有模板方法和基本方法,这个参考上一章节的模板方法模式。在例子中,BenzModel和BMWModel就属于产品类。 +- Builder抽象建造者:规范产品的组建,一般是由子类实现。在例子中,CarBuilder属于抽象建造者。 +- ConcreteBuilder具体建造者:实现抽象类定义的所有方法,并且返回一个组件好的对象。在例子中,BenzBuilder和BMWBuilder就属于具体建造者。 +- Director导演:负责安排已有模块的顺序,然后告诉Builder开始建造,在上面的例子中就是我们的老大,牛叉公司找到老大,说我要这个,这个,那个类型的车辆模型,然后老大就把命令传递给我,我和我的团队就开始拼命的建造,于是一个项目建设完毕了。 + +# 建造者模式的应用 +### 1.建造者模式的优点 + * 封装性。使用建造者模式可以使客户端不必知道产品内部组成的细节,如例子中我们就不需要关心每一个具体的模型内部是如何实现的,产生的对象类型就是CarModel。 + * 建造者独立,容易扩展。 + * 便于控制细节风险。由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。 + +### 2.建造者模式的使用场景 + * 相同的方法,不同的执行顺序,产生不同的事件结果时,可以采用建造者模式。 + * 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时,则可以使用该模式。 + * 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式是非常合适。 + * 在对象创建过程中会使用到系统中的一些其它对象,这些对象在产品对象的创建过程中不易得到时,也可以采用建造者模式封装该对象的创建过程。该种场景,只能是一个补偿方法,因为一个对象不容易获得,而在设计阶段竟然没有发觉,而要通过创建者模式柔化创建过程,本身已经违反设计最初目标。 + +### 3.建造者模式的注意事项 +建造者模式关注的是的零件类型和装配工艺(顺序),这是它与工厂方法模式最大不同的地方,虽然同为创建类模式,但是注重点不同。 + +# 建造者模式的扩展 +已经不用扩展了,因为我们在汽车模型制造的例子中已经对建造者模式进行了扩展,引入了模板方法模式,可能大家会比较疑惑,为什么在其他介绍设计模式的书籍上创建者模式并不是这样说的,读者请注意,建造者模式中还有一个角色没有说明,就是零件,建造者怎么去建造一个对象?是零件的组装,组装顺序不同对象效能也不同,这才是建造者模式要表达的核心意义,而怎么才能更好的达到这种效果呢?引入模板方法模式是一个非常简单而有效的办法。 + +大家看到这里估计就开始犯嘀咕了,这个建造者模式和工厂模式非常相似呀,Yes,是的,是非常相似,但是记住一点你就可以游刃有余的使用了:建造者模式最主要功能是基本方法的调用顺序安排,也就是这些基本方法已经实现了,通俗的说就是零件的装配,顺序不同产生的对象也不同;而工厂方法则重点是创建,创建零件时它的主要职责,你要什么对象我创造一个对象出来,组装顺序则不是他关心的。 + +# 最佳实践 +再次说明,在使用建造者模式的时候考虑一下模板方法模式,别孤立的思考一个模式,僵化的套用一个模式会让受害无穷! + \ No newline at end of file diff --git a/src/dp/com/company/chain_of_responsibility/README.md b/src/dp/com/company/chain_of_responsibility/README.md index 9ccc7a6..be4554c 100644 --- a/src/dp/com/company/chain_of_responsibility/README.md +++ b/src/dp/com/company/chain_of_responsibility/README.md @@ -1,18 +1,18 @@ -#责任链模式 (Command Pattern) -定义:Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.(使多个对象都有机会处理请求,从而避免了请求的发送者接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有对象处理它为止。) - - -责任链模式的通用类图如下图: -![Alt text](chainofresponsibility.jpg "责任链模式类图") - -#责任链模式的应用 -##1.责任链模式的优点 -责任链模式非常显著的优点是将请求和处理分开。请求者可以不用知道谁处理的,处理者可以不用知道请求的全貌,两者解耦,提高系统的灵活性。 - - -##2.责任链模式的缺点 -责任链模式有两个非常显著的缺点:一是性能问题,每个请求都是从链头到链尾,特别是在链比较长的时候,性能是一个非常大的问题。而是调试不很方便,特别是链条比较长,环节比较多的时候,由于采用了类似递归的方式,调试的时候逻辑可能比较复杂。 - - -##3.责任链模式的注意事项 +# 责任链模式 (Command Pattern) +定义:Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.(使多个对象都有机会处理请求,从而避免了请求的发送者接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有对象处理它为止。) + + +责任链模式的通用类图如下图: +![Alt text](chainofresponsibility.jpg "责任链模式类图") + +# 责任链模式的应用 +### 1.责任链模式的优点 +责任链模式非常显著的优点是将请求和处理分开。请求者可以不用知道谁处理的,处理者可以不用知道请求的全貌,两者解耦,提高系统的灵活性。 + + +### 2.责任链模式的缺点 +责任链模式有两个非常显著的缺点:一是性能问题,每个请求都是从链头到链尾,特别是在链比较长的时候,性能是一个非常大的问题。二是调试不很方便,特别是链条比较长,环节比较多的时候,由于采用了类似递归的方式,调试的时候逻辑可能比较复杂。 + + +### 3.责任链模式的注意事项 链中节点数量需要控制,避免出现超长链的情况,一般的做法是在Handler中设置一个最大节点数量,在SetNext方法中判断是否是否是超过其阀值,超过则不允许该链建立,避免无意识地破坏系统性能。 \ No newline at end of file diff --git a/src/dp/com/company/command/README.md b/src/dp/com/company/command/README.md index 724ef98..0aba2f1 100644 --- a/src/dp/com/company/command/README.md +++ b/src/dp/com/company/command/README.md @@ -1,27 +1,27 @@ -#命令模式 (Command Pattern) -定义:Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log request , and support undoable operations.(将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。) - - -命令模式 的通用类图如下图: -![Alt text](command.jpg "命令模式 类图") - -从类图中看,我们看到三个角色: - -- Receiver接收者角色:该角色就是干活的角色,命令传递到这里是应该被执行的。 -- Command命令角色:需要执行的所有命令都在这里声明。 -- Invoker调用者角色:接收到命令,并执行命令。 - - -#命令模式的应用 -##1.命令模式的优点 - * 类间解耦:调用者角色与接收者角色之间没有任何依赖关系,调用者实现功能时只须调用Command抽象类的execute方法就可以,不需要了解到底是哪个接收者执行。 - * 可扩展性:Command的子类可以非常容易扩展,而调用者Invoker和高层次的模块Client不产生严重的代码耦合。 - * 命令模式结合其它模式会更优秀:命令模式可以结合责任链模式,实现命令族解析任务;结合模板方法模式,则可以减少Command子类的膨胀问题。 - - -##2.命令模式的缺点 -如果命令很多,Command的子类会膨胀得非常大,这个需要在项目中慎重考虑。 - - -##3.命令模式的使用场景 +# 命令模式 (Command Pattern) +定义:Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log request , and support undoable operations.(将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。) + + +命令模式 的通用类图如下图: +![Alt text](command.jpg "命令模式 类图") + +从类图中看,我们看到三个角色: + +- Receiver接收者角色:该角色就是干活的角色,命令传递到这里是应该被执行的。 +- Command命令角色:需要执行的所有命令都在这里声明。 +- Invoker调用者角色:接收到命令,并执行命令。 + + +# 命令模式的应用 +### 1.命令模式的优点 + * 类间解耦:调用者角色与接收者角色之间没有任何依赖关系,调用者实现功能时只须调用Command抽象类的execute方法就可以,不需要了解到底是哪个接收者执行。 + * 可扩展性:Command的子类可以非常容易扩展,而调用者Invoker和高层次的模块Client不产生严重的代码耦合。 + * 命令模式结合其它模式会更优秀:命令模式可以结合责任链模式,实现命令族解析任务;结合模板方法模式,则可以减少Command子类的膨胀问题。 + + +### 2.命令模式的缺点 +如果命令很多,Command的子类会膨胀得非常大,这个需要在项目中慎重考虑。 + + +### 3.命令模式的使用场景 只要你认为是命令的地方就可以采用命令模式,例如,在GUI开发中,一个按钮的点击是一个命令,就可以采用命令模式;模拟DOS命令的时候,当然也要采用命令模式;触发-反馈机制的处理等。 \ No newline at end of file diff --git a/src/dp/com/company/composite/README.md b/src/dp/com/company/composite/README.md index bd82fee..c6354c7 100644 --- a/src/dp/com/company/composite/README.md +++ b/src/dp/com/company/composite/README.md @@ -1,28 +1,28 @@ -#组合模式(Composite Pattern) -定义:Composite objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.(将对象组合成树形结构以表示“部分-整理”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。) - - -组合模式的通用类图如下图: -![Alt text](composite.jpg "组合模式类图") - -我们先来说说组合模式的几个角色: - -- Component抽象构件角色:定义参加组合对象的共有方法和属性,可以定义一些默认的行为或属性。 -- Leaf叶子构件:叶子对象,其下再也没有其他的分支,也就是遍历的最小单位。 -- Composite树枝构件:树枝对象,它的作用是组合树枝节点和叶子节点形成一个树形结构。 - - -#组合模式的应用 -##1.组合模式的优点 - * 高层模块调用简单:一颗树形机构中的所有节点都是Component,局部和整体对调用者来说没有任何区别,也就是说,高层模块不必关心自己处理的单个对象还是整个组合结构,简化了高层模块的代码。 - * 节点自由增加:使用了组合模式后,我们可以看看,如果想增加一个树枝节点、树叶节点是不是都很容易,只要找到它的父节点就成,非常容易扩展,符合开闭原则,对以后的维护非常有利。 - - -##2.组合模式的缺点 -组合模式有一个非常明显的缺点,看到我们在场景类中的定义,提到树枝和树叶使用时的定义了吗?直接使用了实现类,这在面向接口编程上是很不恰当的,与依赖倒置原则冲突,读者在使用的时候要考虑清楚,它限制了你接口的影响范围。 - - -##3.组合模式的使用场景 - * 维护和展示部分—整体关系得场景,如树形菜单、文件和文件夹管理。 - * 从一个整体中能够独立出部分模块或功能的场景。 - +# 组合模式(Composite Pattern) +定义:Composite objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.(将对象组合成树形结构以表示“部分-整理”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。) + + +组合模式的通用类图如下图: +![Alt text](composite.jpg "组合模式类图") + +我们先来说说组合模式的几个角色: + +- Component抽象构件角色:定义参加组合对象的共有方法和属性,可以定义一些默认的行为或属性。 +- Leaf叶子构件:叶子对象,其下再也没有其他的分支,也就是遍历的最小单位。 +- Composite树枝构件:树枝对象,它的作用是组合树枝节点和叶子节点形成一个树形结构。 + + +# 组合模式的应用 +### 1.组合模式的优点 + * 高层模块调用简单:一颗树形机构中的所有节点都是Component,局部和整体对调用者来说没有任何区别,也就是说,高层模块不必关心自己处理的单个对象还是整个组合结构,简化了高层模块的代码。 + * 节点自由增加:使用了组合模式后,我们可以看看,如果想增加一个树枝节点、树叶节点是不是都很容易,只要找到它的父节点就成,非常容易扩展,符合开闭原则,对以后的维护非常有利。 + + +### 2.组合模式的缺点 +组合模式有一个非常明显的缺点,看到我们在场景类中的定义,提到树枝和树叶使用时的定义了吗?直接使用了实现类,这在面向接口编程上是很不恰当的,与依赖倒置原则冲突,读者在使用的时候要考虑清楚,它限制了你接口的影响范围。 + + +### 3.组合模式的使用场景 + * 维护和展示部分—整体关系得场景,如树形菜单、文件和文件夹管理。 + * 从一个整体中能够独立出部分模块或功能的场景。 + diff --git a/src/dp/com/company/decorator/README.md b/src/dp/com/company/decorator/README.md index 6359900..c4a447d 100644 --- a/src/dp/com/company/decorator/README.md +++ b/src/dp/com/company/decorator/README.md @@ -1,28 +1,28 @@ -#装饰模式 (Decorate Pattern) -定义:Attach additional responsibilities to an object dynamically keeping the same interface. Decorators provide a flexible alternative to subclassing for extending functionality.(动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生产子类更为灵活。) - - -装饰模式的通用类图如下图: -![Alt text](decorator.jpg "装饰模式类图") - -在类图中,有四个角色需要说明: - -- Component抽象构件:Component是一个接口或者是抽象类,就是定义我们最核心的对象,也就是最原始的对象。 -- ConcreteComponent具体构件:ConcreteComponent是最核心、最原始、最基本的接口或抽象类的实现,你要装饰的就是它。 -- Decorator装饰角色:一般是一个抽象类,做什么用呢?实现接口或者抽象方法,它里面可不一定有抽象的方法呀,在它的属性里必然有一个private变量指向Component抽象构件。 -- 具体装饰对象:ConcreteDecorator1和ConcreteDecorator2是两个具体的装饰类,你要把最核心的、最原始的、最基本的东西装饰城其他东西。 - -#装饰模式的应用 -##1.装饰模式的优点 - * 装饰类与被装饰类可以独立发展,而不会耦合。换句话说,Component类无须知道Decorator类,Decorator类是从外部来扩展Component类的功能,而Decorator类也不用知道具体的构件。 - * 装饰模式时继承关系得一个替代方案。我们看装饰类Decorator,不管装饰多少层,返回的对象还是Component,实现的还是is-a的关系。 - * 装饰模式可以动态地扩展一个实现类得功能。 - -##2.装饰模式的缺点 -对于装饰模式记住一点就足够了:多层的装饰是很复杂的。因此,尽量减少装饰类的数量,以便降低系统的复杂度。 - - -##3.装饰模式的使用场景 - * 需要扩展一个类的功能,或给一个类增加附加功能。 - * 需要动态地给一个对象增加功能,这些功能可以再动态地撤销。 +# 装饰模式 (Decorate Pattern) +定义:Attach additional responsibilities to an object dynamically keeping the same interface. Decorators provide a flexible alternative to subclassing for extending functionality.(动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生产子类更为灵活。) + + +装饰模式的通用类图如下图: +![Alt text](decorator.jpg "装饰模式类图") + +在类图中,有四个角色需要说明: + +- Component抽象构件:Component是一个接口或者是抽象类,就是定义我们最核心的对象,也就是最原始的对象。 +- ConcreteComponent具体构件:ConcreteComponent是最核心、最原始、最基本的接口或抽象类的实现,你要装饰的就是它。 +- Decorator装饰角色:一般是一个抽象类,做什么用呢?实现接口或者抽象方法,它里面可不一定有抽象的方法呀,在它的属性里必然有一个private变量指向Component抽象构件。 +- 具体装饰对象:ConcreteDecorator1和ConcreteDecorator2是两个具体的装饰类,你要把最核心的、最原始的、最基本的东西装饰城其他东西。 + +# 装饰模式的应用 +### 1.装饰模式的优点 + * 装饰类与被装饰类可以独立发展,而不会耦合。换句话说,Component类无须知道Decorator类,Decorator类是从外部来扩展Component类的功能,而Decorator类也不用知道具体的构件。 + * 装饰模式时继承关系得一个替代方案。我们看装饰类Decorator,不管装饰多少层,返回的对象还是Component,实现的还是is-a的关系。 + * 装饰模式可以动态地扩展一个实现类得功能。 + +### 2.装饰模式的缺点 +对于装饰模式记住一点就足够了:多层的装饰是很复杂的。因此,尽量减少装饰类的数量,以便降低系统的复杂度。 + + +### 3.装饰模式的使用场景 + * 需要扩展一个类的功能,或给一个类增加附加功能。 + * 需要动态地给一个对象增加功能,这些功能可以再动态地撤销。 * 需要为一批的兄弟类进行改装或加装功能,当然是首选装饰模式。 \ No newline at end of file diff --git a/src/dp/com/company/dip/README.md b/src/dp/com/company/dip/README.md index 5afb49a..a23dfc8 100644 --- a/src/dp/com/company/dip/README.md +++ b/src/dp/com/company/dip/README.md @@ -1,12 +1,14 @@ -#依赖倒置原则(DIP:Dependence Inversion Principle) -定义:抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。 - -依赖倒置原则要求我们在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等,而不要用具体类来做这些事情。 - -依赖倒置原则的本质就是通过抽象(接口或抽象类)使各个类或模块的实现彼此独立,不互相影响,实现模块间的松耦合。在项目中使用,我们只要遵循以下几个规则就可以. -##最佳实践 - * 每个类尽量都有接口或抽象类,或者抽象类和接口两者都具备。 - * 变量的表名类型尽量是接口或者抽象类。 - * 任何类都不应该从具体类派生。 - * 尽量不要覆写基类的方法。 +# 依赖倒置原则(DIP:Dependence Inversion Principle) +定义:抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。 + +依赖倒置原则要求我们在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等,而不要用具体类来做这些事情。 + +依赖倒置原则的本质就是通过抽象(接口或抽象类)使各个类或模块的实现彼此独立,不互相影响,实现模块间的松耦合。在项目中使用,我们只要遵循以下几个规则就可以. + + +### 最佳实践 + * 每个类尽量都有接口或抽象类,或者抽象类和接口两者都具备。 + * 变量的表名类型尽量是接口或者抽象类。 + * 任何类都不应该从具体类派生。 + * 尽量不要覆写基类的方法。 * 结合里氏替换原则使用。 \ No newline at end of file diff --git a/src/dp/com/company/facade/README.md b/src/dp/com/company/facade/README.md index a85c9ae..98f7ea4 100644 --- a/src/dp/com/company/facade/README.md +++ b/src/dp/com/company/facade/README.md @@ -1,22 +1,22 @@ -#门面模式(Facade Pattern) - 定义:Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.(要求一个子系统的外部与其内部的通讯必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。) - - -门面模式注重“统一的对象”,也就是提供一个访问子系统的接口,除了这个接口不允许有任何访问子系统的行为方式。门面模式示意图如下图: -![Alt text](facade.gif "门面模式示意图") - - - 我们先明确一下门面模式的角色。 - -- Facade门面角色:客户端可以调用这个角色的方法。此角色知晓子系统的所有功能和责任。一般情况下,本角色会将所有从客户端发来的请求委派到相应的子系统去,也就说该角色没有实际的业务逻辑,只是一个委托类。 -- subsystem子系统角色:可以同时有一个或者多个子系统。每一个子系统都不是一个单独的类,而是一个类的集合。子系统并不知道门面的存在。对于子系统而言,门面仅仅是另外一个客户端而已。 - - -#门面模式的应用 -##1.门面模式的优点 - * 减少系统的相互依赖。 - * 提高了灵活性。不管子系统内部如何变化,只要不影响门面对象,任你自由活动。 - * 提高安全性。想让你访问子系统的哪些业务就开通哪些逻辑,不在门面上开通的方法,你休想访问到。 - -##2.门面模式的缺点 +# 门面模式(Facade Pattern) + 定义:Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.(要求一个子系统的外部与其内部的通讯必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。) + + +门面模式注重“统一的对象”,也就是提供一个访问子系统的接口,除了这个接口不允许有任何访问子系统的行为方式。门面模式示意图如下图: +![Alt text](facade.gif "门面模式示意图") + + + 我们先明确一下门面模式的角色。 + +- Facade门面角色:客户端可以调用这个角色的方法。此角色知晓子系统的所有功能和责任。一般情况下,本角色会将所有从客户端发来的请求委派到相应的子系统去,也就说该角色没有实际的业务逻辑,只是一个委托类。 +- subsystem子系统角色:可以同时有一个或者多个子系统。每一个子系统都不是一个单独的类,而是一个类的集合。子系统并不知道门面的存在。对于子系统而言,门面仅仅是另外一个客户端而已。 + + +# 门面模式的应用 +### 1.门面模式的优点 + * 减少系统的相互依赖。 + * 提高了灵活性。不管子系统内部如何变化,只要不影响门面对象,任你自由活动。 + * 提高安全性。想让你访问子系统的哪些业务就开通哪些逻辑,不在门面上开通的方法,你休想访问到。 + +### 2.门面模式的缺点 门面模式最大的缺点就是不符合开闭原则。 \ No newline at end of file diff --git a/src/dp/com/company/factorya/README.md b/src/dp/com/company/factorya/README.md index 3ea08f7..e97b60b 100644 --- a/src/dp/com/company/factorya/README.md +++ b/src/dp/com/company/factorya/README.md @@ -1,30 +1,30 @@ -#抽象工厂模式(Abstract Factory Pattern) -定义:Provide an interface for creating families of related or dependent objects without specifying their concrete classes。为创建一组相关或相互依赖的对象提供一个接口,而且无需指定它们的具体类。 - -抽象工厂模式的类图如下图: -![Alt text](factorya.jpg "抽象工厂模式类图") - - -抽象工厂模式是工厂方法模式的升级版本,在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。 - - -#抽象工厂模式应用 -##1.抽象工厂模式的优点 - * 封装性,每个产品的实现类不是高层模块要关系的,要关心的是什么?是接口,是抽象,它不关心对象是如何创建出来,这由谁负责呢?工厂类,只要知道工厂类是谁,我就能创建出一个需要的对象,省时省力,优秀设计就应该如此。 - * 产品族内的约束为非公开状态。例如生产男女比例的问题上,猜想女娲娘娘肯定有自己的打算,不能让女盛男衰,否则女性的优点不就体现不出来了吗?那在抽象工厂模式,就应该有这样的一个约束:每生产1个女性,就同时生产出1.2个男性,这样的生产过程对调用工厂类的高层模块来说是透明的,它不需要知道这个约束,我就是要一个黄色女性产品就可以了,具体的产品族内的约束是在工厂内实现的。 - - -##2.抽象工厂模式的缺点 -抽象工厂模式的最大缺点就是产品族扩展非常困难,为什么这么说呢?我们以通用代码为例,如果要增加一个产品C,也就是说有产品家族由原来的2个,增加到3个,看看我们的程序有多大改动吧!抽象类AbstractCreator要增加一个方法createProductC(),然后,两个实现类都要修改,想想看,这在项目中的话,还这么让人活!严重违反了开闭原则,而且我们一直说明抽象类和接口是一个契约,改变契约,所有与契约有关系的代码都要修改,这段代码叫什么?叫“有毒代码”,——只要这段代码有关系,就可能产生侵害的危险! - - -##3.抽象工厂模式的使用场景 -抽象工厂模式的使用场景定义非常简单:一个对象族(或是一组没有任何关系的对象)都有相同的约束,则可以使用抽象工厂模式,什么意思呢?例如一个文本编辑器和一个图片处理器,都是软件实体,但是*nix下的文本编辑器和WINDOWS下的文本编辑器虽然功能和界面都相同,但是代码实现是不同的,图片处理器也是类似情况,也就是具有了共同的约束条件:操作系统类型,于是我们可以使用抽象工厂模式,产生不同操作系统下的编辑器和图片处理器。 - - -##4.抽象工厂模式的注意实现 -在抽象工厂模式的缺点中,我们提到抽象工厂模式的产品族扩展比较困难,但是一定要清楚是产品族扩展困难,而不是产品等级,在该模式下,产品等级是非常容易扩展的,增加一个产品等级,只要增加一个工厂类负责新增加出来的产品生产任务即可,也就是说横向扩展容易,纵向扩展困难。以人类为例子, 产品等级中只要男、女两个性别,现实世界还有一种性别:双性人,即使男人也是女人(俗语就是阴阳人),那我们要扩展这个产品等级也是非常容易的,增加三个产品类,分别对应不同的肤色,然后再创建一个工厂类,专门负责不同肤色人的双性人的创建任务,完全通过扩展来实现的需求的变更,从这一点上看,抽象工厂模式是符合开闭原则的。 - - -#最佳实践 -一个模式在什么情况下才能够使用,是很多读者比较困惑的地方,抽象工厂模式是一个简单的模式,使用的场景非常多,大家在软件产品开发过程中,涉及到不同操作系统的时候,都可以考虑使用抽象工厂模式,例如一个应用,需要在三个不同平台上运行:Windows、Linux、Android(Google发布的智能终端操作系统)上运行,你会怎么设计?分别设计三套不同的应用?非也非也,通过抽象工厂模式屏蔽掉操作系统对应用的影响。三个不同操作系统上的软件功能、应用逻辑、UI都应该是非常类似,唯一不同的是调用不同的工厂方法,由不同的产品类去处理与操作系统交互的信息。 +# 抽象工厂模式(Abstract Factory Pattern) +定义:Provide an interface for creating families of related or dependent objects without specifying their concrete classes。为创建一组相关或相互依赖的对象提供一个接口,而且无需指定它们的具体类。 + +抽象工厂模式的类图如下图: +![Alt text](factorya.jpg "抽象工厂模式类图") + + +抽象工厂模式是工厂方法模式的升级版本,在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。 + + +# 抽象工厂模式应用 +### 1.抽象工厂模式的优点 + * 封装性,每个产品的实现类不是高层模块要关系的,要关心的是什么?是接口,是抽象,它不关心对象是如何创建出来,这由谁负责呢?工厂类,只要知道工厂类是谁,我就能创建出一个需要的对象,省时省力,优秀设计就应该如此。 + * 产品族内的约束为非公开状态。例如生产男女比例的问题上,猜想女娲娘娘肯定有自己的打算,不能让女盛男衰,否则女性的优点不就体现不出来了吗?那在抽象工厂模式,就应该有这样的一个约束:每生产1个女性,就同时生产出1.2个男性,这样的生产过程对调用工厂类的高层模块来说是透明的,它不需要知道这个约束,我就是要一个黄色女性产品就可以了,具体的产品族内的约束是在工厂内实现的。 + + +### 2.抽象工厂模式的缺点 +抽象工厂模式的最大缺点就是产品族扩展非常困难,为什么这么说呢?我们以通用代码为例,如果要增加一个产品C,也就是说有产品家族由原来的2个,增加到3个,看看我们的程序有多大改动吧!抽象类AbstractCreator要增加一个方法createProductC(),然后,两个实现类都要修改,想想看,这在项目中的话,还这么让人活!严重违反了开闭原则,而且我们一直说明抽象类和接口是一个契约,改变契约,所有与契约有关系的代码都要修改,这段代码叫什么?叫“有毒代码”,——只要这段代码有关系,就可能产生侵害的危险! + + +### 3.抽象工厂模式的使用场景 +抽象工厂模式的使用场景定义非常简单:一个对象族(或是一组没有任何关系的对象)都有相同的约束,则可以使用抽象工厂模式,什么意思呢?例如一个文本编辑器和一个图片处理器,都是软件实体,但是*nix下的文本编辑器和WINDOWS下的文本编辑器虽然功能和界面都相同,但是代码实现是不同的,图片处理器也是类似情况,也就是具有了共同的约束条件:操作系统类型,于是我们可以使用抽象工厂模式,产生不同操作系统下的编辑器和图片处理器。 + + +### 4.抽象工厂模式的注意实现 +在抽象工厂模式的缺点中,我们提到抽象工厂模式的产品族扩展比较困难,但是一定要清楚是产品族扩展困难,而不是产品等级,在该模式下,产品等级是非常容易扩展的,增加一个产品等级,只要增加一个工厂类负责新增加出来的产品生产任务即可,也就是说横向扩展容易,纵向扩展困难。以人类为例子, 产品等级中只要男、女两个性别,现实世界还有一种性别:双性人,即使男人也是女人(俗语就是阴阳人),那我们要扩展这个产品等级也是非常容易的,增加三个产品类,分别对应不同的肤色,然后再创建一个工厂类,专门负责不同肤色人的双性人的创建任务,完全通过扩展来实现的需求的变更,从这一点上看,抽象工厂模式是符合开闭原则的。 + + +# 最佳实践 +一个模式在什么情况下才能够使用,是很多读者比较困惑的地方,抽象工厂模式是一个简单的模式,使用的场景非常多,大家在软件产品开发过程中,涉及到不同操作系统的时候,都可以考虑使用抽象工厂模式,例如一个应用,需要在三个不同平台上运行:Windows、Linux、Android(Google发布的智能终端操作系统)上运行,你会怎么设计?分别设计三套不同的应用?非也非也,通过抽象工厂模式屏蔽掉操作系统对应用的影响。三个不同操作系统上的软件功能、应用逻辑、UI都应该是非常类似,唯一不同的是调用不同的工厂方法,由不同的产品类去处理与操作系统交互的信息。 diff --git a/src/dp/com/company/factorys/README.md b/src/dp/com/company/factorys/README.md index 39474e3..d78f4ee 100644 --- a/src/dp/com/company/factorys/README.md +++ b/src/dp/com/company/factorys/README.md @@ -1,30 +1,30 @@ -#工厂方法模式(Factory Pattern) -定义:Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses。定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。 - -工厂方法模式的类图如下图: -![Alt text](factorys.jpg "工厂方法模式类图") - - -在工厂方法模式中,抽象产品类Product负责定义产品的共性,实现对事物最抽象定义;Creator为抽象创建类,也就是抽象工厂,具体如何创建产品类是由具体的实现工厂ConcreteCreator完成的。工厂方法模式的变种较多。 - - -#工厂方法模式的应用 -##1.工厂方法模式的优点 - * 首先,良好的封装性,代码结构清晰。一个对象创建是有条件约束的,如一个调用者需要一个具体的产品对象,只要知道这个产品的类名(或约束字符串)就可以了,不用知道创建对象的艰辛过程,减少模块间的耦合。 - * 其次,工厂方法模式的扩展性非常优秀。在增加产品类的情况下,只要适当地修改具体的工厂类或扩展一个工厂类,就可以完成“拥抱变化”。 - * 再次,屏蔽产品类。这一特点非常重要,产品类的实现如何变化,调用者都不需要关心,它只需要关心产品的接口,只要接口保持不表,系统中的上层模块就不要发生变化,因为产品类的实例化工作是由工厂类负责,一个产品对象具体由哪一个产品生成是由工厂类决定的。在数据库开发中,大家应该能够深刻体会到工厂方法模式的好处:如果使用JDBC连接数据库,数据库从MySql切换到Oracle,需要改动地方就是切换一下驱动名称(前提条件是SQL语句是标准语句),其他的都不需要修改,这是工厂方法模式灵活性的一个直接案例。 - * 最后,工厂方法模式是典型的解耦框架。高层模块值需要知道产品的抽象类,其他的实现类都不用关心,符合迪米特原则,我不需要的就不要去交流;也符合依赖倒转原则,只依赖产品类的抽象;当然也符合里氏替换原则,使用产品子类替换产品父类,没问题! - - -##2.工厂方法模式的使用场景 - * 首先,工厂方法模式是new一个对象的替代品,所以在所有需要生成对象的地方都可以使用,但是需要慎重地考虑是否要增加一个工厂类进行管理,增加代码的复杂度。 - * 其次,需要灵活的、可扩展的框架时,可以考虑采用工厂方法模式。万物皆对象,那万物也就皆产品类,例如需要设计一个连接邮件服务器的框架,有三种网络协议可供选择:POP3、IMAP、HTTP,我们就可以把这三种连接方法作为产品类,定义一个接口如IConnectMail,然后定义对邮件的操作方法,三个具体的产品类(也就是连接方式)进行不同的实现,再定义一个工厂方法,按照不同的传入条件,选择不同的连接方式。如此设计,可以做到完美的扩展,如某些邮件服务器提供了WebService接口,很好,我们只要增加一个产品类就可以了。 - * 再次,工厂方法模式可以用在异构项目中,例如通过WebService与一个非Java的项目交互,虽然WebService号称是可以做到异构系统的同构化,但是在实际的开发中,还是会碰到很多问题,如类型问题、WSDL文件的支持问题,等等,从WSDL中产生的对象都认为是一个产品,然后由一个具体的工厂类进行管理,减少与外围系统的耦合。 - * 最后,可以使用在测试驱动开发的框架下,例如,测试一个类A,就需要把与类A有关联关系的类B也同时产生出来,我们可以使用工厂方法模式把类B虚拟出来,避免类A与类B的耦合。目前由于JMock和EasyMock的诞生,该使用场景已经弱化了,读者可以在遇到此种情况时直接考虑使用JMock或EasyMock。 - - -#工厂方法模式的扩展 - * 缩小为简单工厂模式 - * 升级为多个工厂类 - * 替代单例模式 +# 工厂方法模式(Factory Pattern) +定义:Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses。定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。 + +工厂方法模式的类图如下图: +![Alt text](factorys.jpg "工厂方法模式类图") + + +在工厂方法模式中,抽象产品类Product负责定义产品的共性,实现对事物最抽象定义;Creator为抽象创建类,也就是抽象工厂,具体如何创建产品类是由具体的实现工厂ConcreteCreator完成的。工厂方法模式的变种较多。 + + +# 工厂方法模式的应用 +### 1.工厂方法模式的优点 + * 首先,良好的封装性,代码结构清晰。一个对象创建是有条件约束的,如一个调用者需要一个具体的产品对象,只要知道这个产品的类名(或约束字符串)就可以了,不用知道创建对象的艰辛过程,减少模块间的耦合。 + * 其次,工厂方法模式的扩展性非常优秀。在增加产品类的情况下,只要适当地修改具体的工厂类或扩展一个工厂类,就可以完成“拥抱变化”。 + * 再次,屏蔽产品类。这一特点非常重要,产品类的实现如何变化,调用者都不需要关心,它只需要关心产品的接口,只要接口保持不表,系统中的上层模块就不要发生变化,因为产品类的实例化工作是由工厂类负责,一个产品对象具体由哪一个产品生成是由工厂类决定的。在数据库开发中,大家应该能够深刻体会到工厂方法模式的好处:如果使用JDBC连接数据库,数据库从MySql切换到Oracle,需要改动地方就是切换一下驱动名称(前提条件是SQL语句是标准语句),其他的都不需要修改,这是工厂方法模式灵活性的一个直接案例。 + * 最后,工厂方法模式是典型的解耦框架。高层模块值需要知道产品的抽象类,其他的实现类都不用关心,符合迪米特原则,我不需要的就不要去交流;也符合依赖倒转原则,只依赖产品类的抽象;当然也符合里氏替换原则,使用产品子类替换产品父类,没问题! + + +### 2.工厂方法模式的使用场景 + * 首先,工厂方法模式是new一个对象的替代品,所以在所有需要生成对象的地方都可以使用,但是需要慎重地考虑是否要增加一个工厂类进行管理,增加代码的复杂度。 + * 其次,需要灵活的、可扩展的框架时,可以考虑采用工厂方法模式。万物皆对象,那万物也就皆产品类,例如需要设计一个连接邮件服务器的框架,有三种网络协议可供选择:POP3、IMAP、HTTP,我们就可以把这三种连接方法作为产品类,定义一个接口如IConnectMail,然后定义对邮件的操作方法,三个具体的产品类(也就是连接方式)进行不同的实现,再定义一个工厂方法,按照不同的传入条件,选择不同的连接方式。如此设计,可以做到完美的扩展,如某些邮件服务器提供了WebService接口,很好,我们只要增加一个产品类就可以了。 + * 再次,工厂方法模式可以用在异构项目中,例如通过WebService与一个非Java的项目交互,虽然WebService号称是可以做到异构系统的同构化,但是在实际的开发中,还是会碰到很多问题,如类型问题、WSDL文件的支持问题,等等,从WSDL中产生的对象都认为是一个产品,然后由一个具体的工厂类进行管理,减少与外围系统的耦合。 + * 最后,可以使用在测试驱动开发的框架下,例如,测试一个类A,就需要把与类A有关联关系的类B也同时产生出来,我们可以使用工厂方法模式把类B虚拟出来,避免类A与类B的耦合。目前由于JMock和EasyMock的诞生,该使用场景已经弱化了,读者可以在遇到此种情况时直接考虑使用JMock或EasyMock。 + + +# 工厂方法模式的扩展 + * 缩小为简单工厂模式 + * 升级为多个工厂类 + * 替代单例模式 * 延迟初始化。何为延迟初始化(Lazy initialization)?一个对象被消费完毕后,并不立刻释放,工厂类保持其初始状态,等待再次被使用。延迟初始化是工厂方法模式的一个扩展应用。 \ No newline at end of file diff --git a/src/dp/com/company/flyweight/README.md b/src/dp/com/company/flyweight/README.md index cbd5b6a..36624f4 100644 --- a/src/dp/com/company/flyweight/README.md +++ b/src/dp/com/company/flyweight/README.md @@ -1,38 +1,38 @@ -#享元模式(Flyweight Pattern) -定义:Use sharing to support large numbers of fine-grained objects efficiently.(使用共享对象可有效地支持大量的细粒度的对象。) - -享元模式的定义为我们提出了两个要求:细粒度的对象和共享对象。要求细粒度对象,那么不可避免地使得对象数量多且性质相近,那我们就将这些对象的信息分为两个部分:内部状态(intrinsic)与外部状态(extrinsic)。 - -- 内部状态:内部状态是对象可共享出来的信息,存储在享元对象内部并且不会随环境改变而改变。 -- 外部状态:外部状态是对象得以依赖的一个标记,是随环境改变而改变的、不可以共享的状态。 - - - 享元模式的通用类图如图所示。 -![Alt text](flyweight.gif "享元模式类图") - - -我们先来看我们享元模式角色名称。 - -- Flyweight抽象享元角色:它简单地说就是一个产品的抽象类,同时定义出对象的外部状态和内部状态的接口和实现。 -- ConcreteFlyweight具体享元角色:具体的一个产品类,实现抽象角色定义的业务。该角色中需要注意的是内部状态处理应该与环境无关,不应该出现一个操作改变了内部状态,同时修改了外部状态,这是绝对不允许的。 -- UnsharedConcreteFlyweight不可共享的享元角色:不存在外部状态或者安全要求不能够使用共享技术的对象,该对象一般不会出现在享元工厂中。 -- FlyweightFactory享元工厂:职责非常简单,就是构造一个池容器,同时提供从池中获得对象的方法。 - -享元模式的目的在于运用共享技术,使得一些细粒度的对象可以共享,我们的设计确实也应该这样,多使用细粒度的对象,便于重构或重用。 - - -#享元模式的应用 -##1.享元模式的优点和缺点 -享元模式是一个非常简单的模式,它可以大大减少应用程序创建的对象,降低程序内存的占用,增强程序的性能,但它同时也提高了系统复杂性,需要分离外部状态和内部状态,而且外部状态具有固化特性,不应该随内部状态改变而改变,否则导致系统的逻辑混乱。 - - -##2.享元模式的缺点 - * 享元模式会引起类膨胀:每个语法都要产生一个非终结符表达式,语法规则比较复杂时,就可能产生大量的类文件,为维护带来了非常多的麻烦。 - * 享元模式采用递归调用方法:每个非终结符表达式只关心与自己有关的表达式,每个表达式需要知道最终的结果,必须一层一层地剥茧,无论是面向过程的语言还是面向对象的语言,递归都是在必要条件下使用的,它导致调试非常复杂。想想看,如果要排查一个语法错误,我们是不是要一个一个断点的调试下去,直到最小的语法单元。 - * 效率问题:享元模式由于使用了大量的循环和递归,效率是个不容忽视的问题,特别是用于解析复杂、冗长的语法时,效率是难以忍受的。 - - -##3.享元模式的使用场景 - * 系统中存在大量的相似对象。 - * 细粒度的对象都具备较接近的外部状态,而且外部状态与环境无关,也就是说对象没有特定身份。 +# 享元模式(Flyweight Pattern) +定义:Use sharing to support large numbers of fine-grained objects efficiently.(使用共享对象可有效地支持大量的细粒度的对象。) + +享元模式的定义为我们提出了两个要求:细粒度的对象和共享对象。要求细粒度对象,那么不可避免地使得对象数量多且性质相近,那我们就将这些对象的信息分为两个部分:内部状态(intrinsic)与外部状态(extrinsic)。 + +- 内部状态:内部状态是对象可共享出来的信息,存储在享元对象内部并且不会随环境改变而改变。 +- 外部状态:外部状态是对象得以依赖的一个标记,是随环境改变而改变的、不可以共享的状态。 + + + 享元模式的通用类图如图所示。 +![Alt text](flyweight.gif "享元模式类图") + + +我们先来看我们享元模式角色名称。 + +- Flyweight抽象享元角色:它简单地说就是一个产品的抽象类,同时定义出对象的外部状态和内部状态的接口和实现。 +- ConcreteFlyweight具体享元角色:具体的一个产品类,实现抽象角色定义的业务。该角色中需要注意的是内部状态处理应该与环境无关,不应该出现一个操作改变了内部状态,同时修改了外部状态,这是绝对不允许的。 +- UnsharedConcreteFlyweight不可共享的享元角色:不存在外部状态或者安全要求不能够使用共享技术的对象,该对象一般不会出现在享元工厂中。 +- FlyweightFactory享元工厂:职责非常简单,就是构造一个池容器,同时提供从池中获得对象的方法。 + +享元模式的目的在于运用共享技术,使得一些细粒度的对象可以共享,我们的设计确实也应该这样,多使用细粒度的对象,便于重构或重用。 + + +# 享元模式的应用 +### 1.享元模式的优点和缺点 +享元模式是一个非常简单的模式,它可以大大减少应用程序创建的对象,降低程序内存的占用,增强程序的性能,但它同时也提高了系统复杂性,需要分离外部状态和内部状态,而且外部状态具有固化特性,不应该随内部状态改变而改变,否则导致系统的逻辑混乱。 + + +### 2.享元模式的缺点 + * 享元模式会引起类膨胀:每个语法都要产生一个非终结符表达式,语法规则比较复杂时,就可能产生大量的类文件,为维护带来了非常多的麻烦。 + * 享元模式采用递归调用方法:每个非终结符表达式只关心与自己有关的表达式,每个表达式需要知道最终的结果,必须一层一层地剥茧,无论是面向过程的语言还是面向对象的语言,递归都是在必要条件下使用的,它导致调试非常复杂。想想看,如果要排查一个语法错误,我们是不是要一个一个断点的调试下去,直到最小的语法单元。 + * 效率问题:享元模式由于使用了大量的循环和递归,效率是个不容忽视的问题,特别是用于解析复杂、冗长的语法时,效率是难以忍受的。 + + +### 3.享元模式的使用场景 + * 系统中存在大量的相似对象。 + * 细粒度的对象都具备较接近的外部状态,而且外部状态与环境无关,也就是说对象没有特定身份。 * 需要缓冲池的场景。 \ No newline at end of file diff --git a/src/dp/com/company/interpreter/README.md b/src/dp/com/company/interpreter/README.md index 4312b2d..b69bcfe 100644 --- a/src/dp/com/company/interpreter/README.md +++ b/src/dp/com/company/interpreter/README.md @@ -1,35 +1,35 @@ -#解释器模式(Interpreter Pattern) -定义:Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language。(给定一个语言, 定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。) - - - 解释器模式的通用类图如图所示。 -![Alt text](interpreter.jpg "解释器模式类图") - - -- AbstractExpression 抽象解释器:具体的解释任务由各个实现类完成,具体的解释器分别由TerminalExpression和NonterminalExpression完成。 -- TerminalExpression终结符表达式: 实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。具体到我们例子就是VarExpression类,表达式中的每个终结符都在堆栈中产生了一个VarExpression对象。 -- NonterminalExpression非终结符表达式:文法中的每条规则对应于一个非终结表达式,具体到我们的例子就是加减法规则分别对应到AddExpression和SubExpression两个类。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。 -- Context 环境角色:具体到我们的例子中是采用HashMap代替。 - - -#解释器模式的应用 -##1.解释器模式的优点 -解释器是一个简单语法分析工具,它最显著的优点就是扩展性,修改语法规则只要修改相应的非终结符表达式就可以了,若扩展语法,则只要增加非终结符类就可以了。 - - -##2.解释器模式的缺点 - * 解释器模式会引起类膨胀:每个语法都要产生一个非终结符表达式,语法规则比较复杂时,就可能产生大量的类文件,为维护带来了非常多的麻烦。 - * 解释器模式采用递归调用方法:每个非终结符表达式只关心与自己有关的表达式,每个表达式需要知道最终的结果,必须一层一层地剥茧,无论是面向过程的语言还是面向对象的语言,递归都是在必要条件下使用的,它导致调试非常复杂。想想看,如果要排查一个语法错误,我们是不是要一个一个断点的调试下去,直到最小的语法单元。 - * 效率问题:解释器模式由于使用了大量的循环和递归,效率是个不容忽视的问题,特别是用于解析复杂、冗长的语法时,效率是难以忍受的。 - - -##3.解释器模式的使用场景 - * 重复发生的问题可以使用解释器模式:例如,多个应用服务器,每天产生大量的日志,需要对日志文件进行分析处理,由于各个服务器的日志格式不同,但是数据要素是相同的,按照解释器的说法就是终结符表达式都是相同的,但是非终结符表达式就需要制定了。在这种情况下,可以通过程序来一劳永逸地解决该问题。 - * 一个简单语法需要解释的场景:为什么是简单?看看非终结表达式,文法规则越多,复杂度越高,而且类间还要进行递归调用(看看我们例子中的堆栈),不是一般地复杂。想想看,多个类之间的调用你需要什么样的耐心和信心去排查问题。因此,解释器模式一般用来解析比较标准的字符集,例如SQL语法分析,不过该部分逐渐被专用工具所取代。在某些特用的商业环境下也会采用解释器模式,我们刚刚的例子就是一个商业环境,而且现在模型运算的例子非常多,目前很多商业机构已经能够提供出大量的数据进行分析。 - - -##4.解释器模式的注意事项 -尽量不要在重要的模块中使用解释器模式,否则维护会是一个很大的问题。在项目中可以使用shell、JRuby、Groovy等脚本语言来代替解释器模式,弥补Java编译型语言的不足。我们在一个银行的分析型项目中就采用JRuby进行运算处理,避免使用解释器模式的四则运算,效率和性能各方面表现良好。 - -##5.最佳实践 +# 解释器模式(Interpreter Pattern) +定义:Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language。(给定一个语言, 定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。) + + + 解释器模式的通用类图如图所示。 +![Alt text](interpreter.jpg "解释器模式类图") + + +- AbstractExpression 抽象解释器:具体的解释任务由各个实现类完成,具体的解释器分别由TerminalExpression和NonterminalExpression完成。 +- TerminalExpression终结符表达式: 实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。具体到我们例子就是VarExpression类,表达式中的每个终结符都在堆栈中产生了一个VarExpression对象。 +- NonterminalExpression非终结符表达式:文法中的每条规则对应于一个非终结表达式,具体到我们的例子就是加减法规则分别对应到AddExpression和SubExpression两个类。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。 +- Context 环境角色:具体到我们的例子中是采用HashMap代替。 + + +# 解释器模式的应用 +### 1.解释器模式的优点 +解释器是一个简单语法分析工具,它最显著的优点就是扩展性,修改语法规则只要修改相应的非终结符表达式就可以了,若扩展语法,则只要增加非终结符类就可以了。 + + +### 2.解释器模式的缺点 + * 解释器模式会引起类膨胀:每个语法都要产生一个非终结符表达式,语法规则比较复杂时,就可能产生大量的类文件,为维护带来了非常多的麻烦。 + * 解释器模式采用递归调用方法:每个非终结符表达式只关心与自己有关的表达式,每个表达式需要知道最终的结果,必须一层一层地剥茧,无论是面向过程的语言还是面向对象的语言,递归都是在必要条件下使用的,它导致调试非常复杂。想想看,如果要排查一个语法错误,我们是不是要一个一个断点的调试下去,直到最小的语法单元。 + * 效率问题:解释器模式由于使用了大量的循环和递归,效率是个不容忽视的问题,特别是用于解析复杂、冗长的语法时,效率是难以忍受的。 + + +### 3.解释器模式的使用场景 + * 重复发生的问题可以使用解释器模式:例如,多个应用服务器,每天产生大量的日志,需要对日志文件进行分析处理,由于各个服务器的日志格式不同,但是数据要素是相同的,按照解释器的说法就是终结符表达式都是相同的,但是非终结符表达式就需要制定了。在这种情况下,可以通过程序来一劳永逸地解决该问题。 + * 一个简单语法需要解释的场景:为什么是简单?看看非终结表达式,文法规则越多,复杂度越高,而且类间还要进行递归调用(看看我们例子中的堆栈),不是一般地复杂。想想看,多个类之间的调用你需要什么样的耐心和信心去排查问题。因此,解释器模式一般用来解析比较标准的字符集,例如SQL语法分析,不过该部分逐渐被专用工具所取代。在某些特用的商业环境下也会采用解释器模式,我们刚刚的例子就是一个商业环境,而且现在模型运算的例子非常多,目前很多商业机构已经能够提供出大量的数据进行分析。 + + +### 4.解释器模式的注意事项 +尽量不要在重要的模块中使用解释器模式,否则维护会是一个很大的问题。在项目中可以使用shell、JRuby、Groovy等脚本语言来代替解释器模式,弥补Java编译型语言的不足。我们在一个银行的分析型项目中就采用JRuby进行运算处理,避免使用解释器模式的四则运算,效率和性能各方面表现良好。 + +# 最佳实践 解释器模式在实际的系统开发中使用的非常少,因为它会引起效率、性能以及维护等问题,一般在大中型的框架型项目能够找到它的身影,比如一些数据分析工具、报表设计工具、科学计算工具等等,若你确实遇到“一种特定类型的问题发生的频率足够高”的情况,准备使用解释器模式时,可以考虑一下Expression4J、MESP(Math Expression String Parser)、Jep等开源的解析工具包(这三个开源产品都可以百度、Google中搜索到,请读者自行查询),功能都异常强大,而且非常容易使用,效率也还不错,实现大多数的数学运算完全没有问题,自己没有必要从头开始编写解释器,有人已经建立了一条康庄大道,何必再走自己的泥泞小路呢? \ No newline at end of file diff --git a/src/dp/com/company/isp/README.md b/src/dp/com/company/isp/README.md index 3550e02..6e4a2cc 100644 --- a/src/dp/com/company/isp/README.md +++ b/src/dp/com/company/isp/README.md @@ -1,20 +1,21 @@ -#接口隔离原则(ISP:Interface Segregation Principle) -定义:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。 - -根据接口隔离原则,当一个接口太大时,我们需要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法即可。每一个接口应该承担一种相对独立的角色,不干不该干的事,该干的事都要干。 -看到这里好像接口隔离原则与单一职责原则是相同的。其实接口隔离原则与单一职责原则的审视角度是不相同的,单一职责原则要求的是类和接口职责单一,注重的是职责,这是业务逻辑上的划分,而接口隔离原则要求接口的方法尽量少。 - -##接口隔离原则是对接口进行规范约束,其包含的以下4层含义: - * 接口要尽量少。 - * 接口要高内聚。 - * 定制服务。 - * 接口设计师有限度的。 - -在使用接口隔离原则时,我们需要注意控制接口的粒度,接口不能太小,如果太小会导致系统中接口泛滥,不利于维护;接口也不能太大,太大的接口将违背接口隔离原则,灵活性较差,使用起来很不方便。一般而言,接口中仅包含为某一类用户定制的方法即可,不应该强迫客户依赖于那些它们不用的方法。 - -接口隔离原则是对接口的定义,同时也是对类的定义,接口和类尽量使用原子接口或原子类来组装。但是,这个原子该怎么划分是设计模式中的一大难题,在实践中可以根据以下几个规则来衡量. -##最佳实践 - * 一个接口只服务于一个子模块或业务逻辑。 - * 通过业务逻辑压缩接口中的public方法,接口时常去回顾,尽量让接口达到“满身筋骨肉”,而不是“肥嘟嘟”的一大堆方法。 - * 已经被污染了的接口,尽量去修改,若变更的风险较大,则采用适配器模式进行转化处理。 +# 接口隔离原则(ISP:Interface Segregation Principle) +定义:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。 + +根据接口隔离原则,当一个接口太大时,我们需要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法即可。每一个接口应该承担一种相对独立的角色,不干不该干的事,该干的事都要干。 +看到这里好像接口隔离原则与单一职责原则是相同的。其实接口隔离原则与单一职责原则的审视角度是不相同的,单一职责原则要求的是类和接口职责单一,注重的是职责,这是业务逻辑上的划分,而接口隔离原则要求接口的方法尽量少。 + +### 接口隔离原则是对接口进行规范约束,其包含的以下4层含义: + * 接口要尽量少。 + * 接口要高内聚。 + * 定制服务。 + * 接口设计师有限度的。 + +在使用接口隔离原则时,我们需要注意控制接口的粒度,接口不能太小,如果太小会导致系统中接口泛滥,不利于维护;接口也不能太大,太大的接口将违背接口隔离原则,灵活性较差,使用起来很不方便。一般而言,接口中仅包含为某一类用户定制的方法即可,不应该强迫客户依赖于那些它们不用的方法。 + +接口隔离原则是对接口的定义,同时也是对类的定义,接口和类尽量使用原子接口或原子类来组装。但是,这个原子该怎么划分是设计模式中的一大难题,在实践中可以根据以下几个规则来衡量. + +#最佳实践 + * 一个接口只服务于一个子模块或业务逻辑。 + * 通过业务逻辑压缩接口中的public方法,接口时常去回顾,尽量让接口达到“满身筋骨肉”,而不是“肥嘟嘟”的一大堆方法。 + * 已经被污染了的接口,尽量去修改,若变更的风险较大,则采用适配器模式进行转化处理。 * 了解环境,拒绝盲从。 环境不同,接口拆分的标准就不同,深入了解业务逻辑,根据实际情况设计接口。 \ No newline at end of file diff --git a/src/dp/com/company/iterator/README.md b/src/dp/com/company/iterator/README.md index 5f6f7f8..35aac9d 100644 --- a/src/dp/com/company/iterator/README.md +++ b/src/dp/com/company/iterator/README.md @@ -1,14 +1,14 @@ -#迭代器模式(Iterator Pattern) -定义:Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.(将一个类的接口变换成客户端所期待的另一种接口,从而是原本因接口不匹配而无法在一起工作的两个类能够在一起工作。) - - -迭代器模式通用类图如图所示。 -![Alt text](iterator.jpg "迭代器模式类图") - - - 我们先来看看迭代器模式中的各个角色: - -- Iterator抽象迭代器:抽象迭代器负责定义访问和遍历元素的接口。 -- ConcreteIterator具体迭代器:具体迭代器角色要实现迭代器接口,完成容器元素的遍历。 -- Aggregate抽象容器:容器角色负责提供创建具体迭代器角色的接口,必然提供一个类似createIterator()这样的方法,在Java中一般是iterator()方法。 +# 迭代器模式(Iterator Pattern) +定义:Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.(将一个类的接口变换成客户端所期待的另一种接口,从而是原本因接口不匹配而无法在一起工作的两个类能够在一起工作。) + + +迭代器模式通用类图如图所示。 +![Alt text](iterator.jpg "迭代器模式类图") + + + 我们先来看看迭代器模式中的各个角色: + +- Iterator抽象迭代器:抽象迭代器负责定义访问和遍历元素的接口。 +- ConcreteIterator具体迭代器:具体迭代器角色要实现迭代器接口,完成容器元素的遍历。 +- Aggregate抽象容器:容器角色负责提供创建具体迭代器角色的接口,必然提供一个类似createIterator()这样的方法,在Java中一般是iterator()方法。 - Concrete Aggregate具体容器:具体容器实现容器接口定义的方法,创建出容纳迭代器的对象。 \ No newline at end of file diff --git a/src/dp/com/company/lod/README.md b/src/dp/com/company/lod/README.md index 57387d1..75ea405 100644 --- a/src/dp/com/company/lod/README.md +++ b/src/dp/com/company/lod/README.md @@ -1,8 +1,8 @@ -#迪米特法则(LoD:Law of Demeter) -定义:一个软件实体应当尽可能少地与其他实体发生相互作用。 - -迪米特法则要求我们在设计系统时,应该尽量减少对象之间的交互,如果两个对象之间不必彼此直接通信,那么这两个对象就不应当发生任何直接的相互作用,如果其中的一个对象需要调用另一个对象的某一个方法的话,可以通过第三者转发这个调用。简言之,就是通过引入一个合理的第三者来降低现有对象之间的耦合度。 - -在将迪米特法则运用到系统设计中时,要注意下面的几点:在类的划分上,应当尽量创建松耦合的类,类之间的耦合度越低,就越有利于复用,一个处在松耦合中的类一旦被修改,不会对关联的类造成太大波及;在类的结构设计上,每一个类都应当尽量降低其成员变量和成员函数的访问权限;在类的设计上,只要有可能,一个类型应当设计成不变类;在对其他类的引用上,一个对象对其他对象的引用应当降到最低。 - +# 迪米特法则(LoD:Law of Demeter) +定义:一个软件实体应当尽可能少地与其他实体发生相互作用。 + +迪米特法则要求我们在设计系统时,应该尽量减少对象之间的交互,如果两个对象之间不必彼此直接通信,那么这两个对象就不应当发生任何直接的相互作用,如果其中的一个对象需要调用另一个对象的某一个方法的话,可以通过第三者转发这个调用。简言之,就是通过引入一个合理的第三者来降低现有对象之间的耦合度。 + +在将迪米特法则运用到系统设计中时,要注意下面的几点:在类的划分上,应当尽量创建松耦合的类,类之间的耦合度越低,就越有利于复用,一个处在松耦合中的类一旦被修改,不会对关联的类造成太大波及;在类的结构设计上,每一个类都应当尽量降低其成员变量和成员函数的访问权限;在类的设计上,只要有可能,一个类型应当设计成不变类;在对其他类的引用上,一个对象对其他对象的引用应当降到最低。 + 迪米特法则的核心观念就是类间解耦,弱耦合,只有弱耦合了以后,类的复用率才可以提高。其要求的结果就是产生了大量的中转或跳转类,导致系统的复杂性提高,同时也为维护带来了难度。我们在采用迪米特法则时需要反复权衡,既做到让结构清晰,又做到高内聚低耦合。 \ No newline at end of file diff --git a/src/dp/com/company/lsp/README.md b/src/dp/com/company/lsp/README.md index df35246..f305d68 100644 --- a/src/dp/com/company/lsp/README.md +++ b/src/dp/com/company/lsp/README.md @@ -1,14 +1,14 @@ -#里氏替换原则(LSP:Liskov Substitution Principle) -定义:所有引用基类(父类)的地方必须能透明地使用其子类的对象。 - -里氏替换原则告诉我们,在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象。 - -里氏替换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。 - -##里氏替换原则为良好的继承定义了一个规范,它包含了4层定义: - * 子类必须完全实现父类的方法。 - * 子类可以有自己的个性。 - * 覆盖或实现父类的方法时输入参数可以被放大。 - * 覆写或实现父类的方法时输出结果可以被缩小。 - +# 里氏替换原则(LSP:Liskov Substitution Principle) +定义:所有引用基类(父类)的地方必须能透明地使用其子类的对象。 + +里氏替换原则告诉我们,在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象。 + +里氏替换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。 + +### 里氏替换原则为良好的继承定义了一个规范,它包含了4层定义: + * 子类必须完全实现父类的方法。 + * 子类可以有自己的个性。 + * 覆盖或实现父类的方法时输入参数可以被放大。 + * 覆写或实现父类的方法时输出结果可以被缩小。 + 采用里氏替换原则的目的就是增强程序的健壮性,版本升级时也可以保持非常好的兼容性。即使增加子类,原有的子类还可以继续运行。在实际项目中,每个子类对应不同的业务含义,使用父类作为参数,传递不同的子类完成不同的业务逻辑,非常完美。 \ No newline at end of file diff --git a/src/dp/com/company/mediator/README.md b/src/dp/com/company/mediator/README.md index 11ff938..7b28077 100644 --- a/src/dp/com/company/mediator/README.md +++ b/src/dp/com/company/mediator/README.md @@ -1,41 +1,41 @@ -#中介者模式(Mediator Pattern) -定义:Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.(用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互。) - - -中介者模式的通用类图如下图: -![Alt text](mediator.jpg "中介者模式类图") - -从类图中看,中介者模式由以下几部分组成: - -- Mediator抽象中介者角色:抽象中介者角色定义统一的接口,用于个同事角色之间的通讯。 -- Concrete Mediator具体中介者角色:具体中介者角色通过协调同事角色实现协作行为,因此它必须依赖于各同事角色。 -- Colleague同事角色:每一个同事角色都知道中介者角色,而且与其它的同事角色通讯的时候,一定要通过中介者角色协作。每个同事类的行为分为两种:一种是同事本身的行为,比如改变对象本身的状态,处理自己的行为等,这种方法叫做自发行为(Self-Method),与其它的同事类或中介者没有任何的依赖;第二种是必须依赖中介者才能完成的行为,叫做依赖方法(Dep-Method)。 - - -#中介者模式应用 -##1.中介者模式的优点 - 中介者模式的优点就是减少类间的依赖,把原有的一对多的依赖变成了一对一的依赖,同事类只依赖中介者,减少了依赖,当然同时也降低了类间的耦合。 - - -##2.中介者模式的缺点 -中介者模式的缺点就是中介者会膨胀得很大,而且逻辑复杂,原本N个对象直接的相互依赖关系转换为中介者和同事类的依赖关系,同事类越多,中介者的逻辑就越复杂。 - - -##3.中介者模式的使用场景 -中介者模式简单,但是简单不代表容易使用,很容易被误用。在面向对象的编程中,对象和对象之间必然会有依赖关系,如果某个类和其他类没有任何相互依赖的关系,那这个类就是一个“孤岛”,在项目中就没有存在的必要了!就像某个人如果永远独立生活,与任何人都没有关系,那这个人基本上就算是野人了——排除在人类这个定义之外。 - 类之间的依赖关系式必然存在的,一个类依赖多个类的情况也是存在的,存在即合理,那是否可以说只要有多个依赖关系就考虑使用中介者模式呢?答案是否定的。中介者模式未必能帮你把原本凌乱的逻辑整理得清清楚楚,而且中介者模式也是有缺点的,这个缺点在使用不当时会被放大,比如原本就简单的几个对象依赖关系,如果为了使用模式而加入了中介者,必然导致中介者的逻辑复杂化,因此中介者模式的使用需要“量力而行”!中介者模式适用于多个对象之间紧密耦合的情况,紧密耦合的标准是:在类图中出现了蜘蛛网状结构。在这种情况下一定要考虑使用中介者模式,这有利于蜘蛛网梳理为星型结构,使原本复杂混乱的关系变得清晰简单。 - - -#中介者模式的实际应用 - * 机场调度中心 - * MVC框架 - * 媒体网关 - * 中介服务 - - -#中介者模式的最佳实践 -中介者模式是一个非常好的封装模式,也是一个很容易被滥用的模式。使用中介者模式必然会带来中介者的膨胀问题。大家可以在如下的情况下尝试使用中介者模式: - -- N个对象之间产生了相互的依赖关系(N>2)。 -- 多个对象有依赖关系,但是依赖的行为尚不确定或者有发生改变的可能,在这种情况下一般建议采用中介者模式,降低变更引起的风险扩散。 +# 中介者模式(Mediator Pattern) +定义:Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.(用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互。) + + +中介者模式的通用类图如下图: +![Alt text](mediator.jpg "中介者模式类图") + +从类图中看,中介者模式由以下几部分组成: + +- Mediator抽象中介者角色:抽象中介者角色定义统一的接口,用于个同事角色之间的通讯。 +- Concrete Mediator具体中介者角色:具体中介者角色通过协调同事角色实现协作行为,因此它必须依赖于各同事角色。 +- Colleague同事角色:每一个同事角色都知道中介者角色,而且与其它的同事角色通讯的时候,一定要通过中介者角色协作。每个同事类的行为分为两种:一种是同事本身的行为,比如改变对象本身的状态,处理自己的行为等,这种方法叫做自发行为(Self-Method),与其它的同事类或中介者没有任何的依赖;第二种是必须依赖中介者才能完成的行为,叫做依赖方法(Dep-Method)。 + + +# 中介者模式应用 +### 1.中介者模式的优点 + 中介者模式的优点就是减少类间的依赖,把原有的一对多的依赖变成了一对一的依赖,同事类只依赖中介者,减少了依赖,当然同时也降低了类间的耦合。 + + +### 2.中介者模式的缺点 +中介者模式的缺点就是中介者会膨胀得很大,而且逻辑复杂,原本N个对象直接的相互依赖关系转换为中介者和同事类的依赖关系,同事类越多,中介者的逻辑就越复杂。 + + +### 3.中介者模式的使用场景 +中介者模式简单,但是简单不代表容易使用,很容易被误用。在面向对象的编程中,对象和对象之间必然会有依赖关系,如果某个类和其他类没有任何相互依赖的关系,那这个类就是一个“孤岛”,在项目中就没有存在的必要了!就像某个人如果永远独立生活,与任何人都没有关系,那这个人基本上就算是野人了——排除在人类这个定义之外。 + 类之间的依赖关系式必然存在的,一个类依赖多个类的情况也是存在的,存在即合理,那是否可以说只要有多个依赖关系就考虑使用中介者模式呢?答案是否定的。中介者模式未必能帮你把原本凌乱的逻辑整理得清清楚楚,而且中介者模式也是有缺点的,这个缺点在使用不当时会被放大,比如原本就简单的几个对象依赖关系,如果为了使用模式而加入了中介者,必然导致中介者的逻辑复杂化,因此中介者模式的使用需要“量力而行”!中介者模式适用于多个对象之间紧密耦合的情况,紧密耦合的标准是:在类图中出现了蜘蛛网状结构。在这种情况下一定要考虑使用中介者模式,这有利于蜘蛛网梳理为星型结构,使原本复杂混乱的关系变得清晰简单。 + + +# 中介者模式的实际应用 + * 机场调度中心 + * MVC框架 + * 媒体网关 + * 中介服务 + + +# 中介者模式的最佳实践 +中介者模式是一个非常好的封装模式,也是一个很容易被滥用的模式。使用中介者模式必然会带来中介者的膨胀问题。大家可以在如下的情况下尝试使用中介者模式: + +- N个对象之间产生了相互的依赖关系(N>2)。 +- 多个对象有依赖关系,但是依赖的行为尚不确定或者有发生改变的可能,在这种情况下一般建议采用中介者模式,降低变更引起的风险扩散。 - 产品开发。一个明显的例子就是MVC框架,把中介者模式应用到产品中,可以提升产品的性能和扩展性,但是对于项目开发就未必,因此项目是以交付投产为目标,而产品则是以稳定、高效、扩展为宗旨。 \ No newline at end of file diff --git a/src/dp/com/company/memento/README.md b/src/dp/com/company/memento/README.md index 26500c7..c06dfbb 100644 --- a/src/dp/com/company/memento/README.md +++ b/src/dp/com/company/memento/README.md @@ -1,29 +1,29 @@ -#备忘录模式(Memento Pattern) - 定义:Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later.(在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。) - - - 备忘录模式的通用类图如图所示。 -![Alt text](memento.jpg "备忘录模式示意图") - - -我们来看看类图中的三个角色。 - -- Originator发起人角色:记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据。 -- Memento备忘录角色:负责存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态。 -- Caretaker备忘录管理员角色:对备忘录进行管理、保存和提供备忘录。 - -#备忘录模式的应用 -##1.备忘录模式的使用场景 - * 需要保存和恢复数据的相关状态场景。 - * 提供一个可回滚的操作;比如word中的CTRL+Z组合键,IE浏览器中的后退按钮,文件管理器上的backspace键等。 - * 需要监控的副本场景中。例如监控一个对象的属性,但是监控又不应该作为系统的主业务来调用,它只是边缘应用,即使出现监控不准、错误报警也影响不大,因此一般的做法是备份一个主线程中的对象,然后由分析程序来分析。 - * 数据库连接的事务管理就是用的备忘录模式。 - -##2.备忘录模式的注意事项 - * 备忘录的生命期:备忘录创建出来就要在“最近”的代码中使用,要主动管理它的生命周期,建立就要使用,不适用就要立刻删除其引用,等待垃圾回收器对它的回收处理。 - * 备忘录的性能:不要在频繁建立备份的场景中使用备忘录模式(比如一个for循环中),原因有二:一是控制不了备忘录建立的对象数量,二是大对象的建立要消耗资源的,系统的性能需要考虑。 - -##3.备忘录模式的扩展 - * clone方式的备忘录 - * 多状态的备忘录模式 +# 备忘录模式(Memento Pattern) + 定义:Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later.(在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。) + + + 备忘录模式的通用类图如图所示。 +![Alt text](memento.jpg "备忘录模式示意图") + + +我们来看看类图中的三个角色。 + +- Originator发起人角色:记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据。 +- Memento备忘录角色:负责存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态。 +- Caretaker备忘录管理员角色:对备忘录进行管理、保存和提供备忘录。 + +#备忘录模式的应用 +### 1.备忘录模式的使用场景 + * 需要保存和恢复数据的相关状态场景。 + * 提供一个可回滚的操作;比如word中的CTRL+Z组合键,IE浏览器中的后退按钮,文件管理器上的backspace键等。 + * 需要监控的副本场景中。例如监控一个对象的属性,但是监控又不应该作为系统的主业务来调用,它只是边缘应用,即使出现监控不准、错误报警也影响不大,因此一般的做法是备份一个主线程中的对象,然后由分析程序来分析。 + * 数据库连接的事务管理就是用的备忘录模式。 + +### 2.备忘录模式的注意事项 + * 备忘录的生命期:备忘录创建出来就要在“最近”的代码中使用,要主动管理它的生命周期,建立就要使用,不适用就要立刻删除其引用,等待垃圾回收器对它的回收处理。 + * 备忘录的性能:不要在频繁建立备份的场景中使用备忘录模式(比如一个for循环中),原因有二:一是控制不了备忘录建立的对象数量,二是大对象的建立要消耗资源的,系统的性能需要考虑。 + +### 3.备忘录模式的扩展 + * clone方式的备忘录 + * 多状态的备忘录模式 * 多备份的备忘录 \ No newline at end of file diff --git a/src/dp/com/company/observer/README.md b/src/dp/com/company/observer/README.md index 0cdbc2c..bc97142 100644 --- a/src/dp/com/company/observer/README.md +++ b/src/dp/com/company/observer/README.md @@ -1,34 +1,34 @@ -#观察者模式(Observer Pattern) - 定义:Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.(定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。) - - -观察者模式的通用类图如下图: -![Alt text](observer.jpg "观察者模式类图") - - 我们先来解释一下观察者模式的几个角色名称: - -- Subject被观察者:定义被观察者必须实现的职责,它必须能够动态地增加、取消观察者。它一般是抽象类或者是实现类,仅仅完成作为被观察这必须实现的职责:管理观察者并通知观察者。 -- Observer观察者:观察者接收到消息后,机进行update(更新方法)操作,对接收到的信息进行处理。 -- ConcreteSubject具体的被观察者:定义被观察者自己的业务逻辑,同时定义对哪些事情进行通知。 -- ConcreteObserver具体的观察者:每个观察在接收到消息后的处理反应是不同,各个观察者有自己的处理逻辑。 - - -#观察者模式的应用 -##1.观察者模式的优点 - * 观察者和被观察者之间是抽象耦合:如此设计,则不管是观察者还是被观察者都非常容易扩展,而且在Java中都已经实现的抽象层级的定义,在系统扩展方面更是得心应手。 - * 建立一套触发机制。 - - -##2.观察者模式的缺点 -观察者模式需要考虑一下开发效率和运行效率问题,一个被观察者,多个观察者,开发和调试就会比较复杂,而且在Java中消息的通知默认是顺序执行,一个观察者卡壳,会影响整体的执行效率。在这种情况下,一般考虑采用异步的方式。 -多级触发时效率更是让人担忧,大家在设计时注意考虑。 - - -##3.观察者模式的使用场景 - * 关联行为场景。需要注意的是,关联行为是可拆分的,而不是“组合”关系。 - * 事件多级触发场景。 - * 跨系统的消息交换场景,如消息队列的处理机制。 - -##4.观察者模式的注意事项 - * 广播链的问题。 +# 观察者模式(Observer Pattern) + 定义:Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.(定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。) + + +观察者模式的通用类图如下图: +![Alt text](observer.jpg "观察者模式类图") + + 我们先来解释一下观察者模式的几个角色名称: + +- Subject被观察者:定义被观察者必须实现的职责,它必须能够动态地增加、取消观察者。它一般是抽象类或者是实现类,仅仅完成作为被观察这必须实现的职责:管理观察者并通知观察者。 +- Observer观察者:观察者接收到消息后,机进行update(更新方法)操作,对接收到的信息进行处理。 +- ConcreteSubject具体的被观察者:定义被观察者自己的业务逻辑,同时定义对哪些事情进行通知。 +- ConcreteObserver具体的观察者:每个观察在接收到消息后的处理反应是不同,各个观察者有自己的处理逻辑。 + + +# 观察者模式的应用 +### 1.观察者模式的优点 + * 观察者和被观察者之间是抽象耦合:如此设计,则不管是观察者还是被观察者都非常容易扩展,而且在Java中都已经实现的抽象层级的定义,在系统扩展方面更是得心应手。 + * 建立一套触发机制。 + + +### 2.观察者模式的缺点 +观察者模式需要考虑一下开发效率和运行效率问题,一个被观察者,多个观察者,开发和调试就会比较复杂,而且在Java中消息的通知默认是顺序执行,一个观察者卡壳,会影响整体的执行效率。在这种情况下,一般考虑采用异步的方式。 +多级触发时效率更是让人担忧,大家在设计时注意考虑。 + + +### 3.观察者模式的使用场景 + * 关联行为场景。需要注意的是,关联行为是可拆分的,而不是“组合”关系。 + * 事件多级触发场景。 + * 跨系统的消息交换场景,如消息队列的处理机制。 + +### 4.观察者模式的注意事项 + * 广播链的问题。 * 异步处理问题。 \ No newline at end of file diff --git a/src/dp/com/company/ocp/README.md b/src/dp/com/company/ocp/README.md index 96d98da..72769ad 100644 --- a/src/dp/com/company/ocp/README.md +++ b/src/dp/com/company/ocp/README.md @@ -1,12 +1,12 @@ -#开闭原则(OCP:Open-Closed Principle) -定义:一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。 - -根据开闭原则,在设计一个软件系统模块(类,方法)的时候,应该可以在不修改原有的模块(修改关闭)的基础上,能扩展其功能(扩展开放)。遵循开闭原则的系统设计,可以让软件系统可复用,并且易于维护。这也是系统设计需要遵循开闭原则的原因: - 1.稳定性。开闭原则要求扩展功能不修改原来的代码,这可以让 软件系统在变化中保持稳定。 - 2.扩展性。开闭原则要求对扩展开放,通过扩展提供新的或改变原有的功能,让软件系统具有灵活的可扩展性。 - -##如何使用开闭原则 - * 抽象约束。首先通过接口或抽象类约束扩展,对扩展进行边界限定,不允许出现在接口或抽象类中不存在的public方法;第二,参数类型,引用对象尽量使用接口或者抽象类,而不是实现类;第三,抽象层尽量保持稳定,一旦确定既不允许修改。 - * 元数据(metadata)控制模块行为。尽量使用元数据(用来描述环境和数据的数据,通俗的说就是配置参数)来控制程序的行为,减少重复开发。 - * 制定项目章程。对于项目来说,约定优于配置。 +# 开闭原则(OCP:Open-Closed Principle) +定义:一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。 + +根据开闭原则,在设计一个软件系统模块(类,方法)的时候,应该可以在不修改原有的模块(修改关闭)的基础上,能扩展其功能(扩展开放)。遵循开闭原则的系统设计,可以让软件系统可复用,并且易于维护。这也是系统设计需要遵循开闭原则的原因: + 1.稳定性。开闭原则要求扩展功能不修改原来的代码,这可以让 软件系统在变化中保持稳定。 + 2.扩展性。开闭原则要求对扩展开放,通过扩展提供新的或改变原有的功能,让软件系统具有灵活的可扩展性。 + +### 如何使用开闭原则 + * 抽象约束。首先通过接口或抽象类约束扩展,对扩展进行边界限定,不允许出现在接口或抽象类中不存在的public方法;第二,参数类型,引用对象尽量使用接口或者抽象类,而不是实现类;第三,抽象层尽量保持稳定,一旦确定既不允许修改。 + * 元数据(metadata)控制模块行为。尽量使用元数据(用来描述环境和数据的数据,通俗的说就是配置参数)来控制程序的行为,减少重复开发。 + * 制定项目章程。对于项目来说,约定优于配置。 * 封装变化。第一,将相同的变化封装到一个接口或抽象类中;第二,将不同的变化封装到不同的接口或抽象类中,不应该有两个不同的变化出现在同一个接口或抽象类中。 \ No newline at end of file diff --git a/src/dp/com/company/pkbehavior/README.md b/src/dp/com/company/pkbehavior/README.md index 7212bbf..5b43523 100644 --- a/src/dp/com/company/pkbehavior/README.md +++ b/src/dp/com/company/pkbehavior/README.md @@ -1,41 +1,41 @@ -#行为类模式大PK ----------- - -#1.命令模式VS策略模式 -       策略模式的意图是封装算法,它认为算法已经是一个完整的、不可拆分的原子业务(注意这里是原子业务,而不是原子对象),即其意图是让这些算法独立,并且可以相互替换,让行为的变化独立于拥有行为的客户;而命令模式则是对动作的解耦,把一个动作的执行分为执行对象(接收者角色)、执行行为(命令角色),让两者相互独立而不相互影响。 - -##1.1最佳实践 -       命令模式和策略模式的类图完全一样,代码实现也比较类似,但是两者还是有区别的。 - -- 关注点不同 -       策略模式关注的是算法的完整性、封装性,只有具备了这两个条件才能保证其可以自由切换。命令模式则关注的是解耦问题。 -- 角色功能不同 -       策略模式中的抽象算法和具体算法与命令模式的接收者非常相似,但是他们的职责不同。策略模式中的具体算法是负责一个完整算法逻辑,它是不可再拆分的原子业务单元,一旦变更就是对算法整体的变更。而命令模式关注的是功能的实现。 -- 使用场景不同 -       策略模式使用于算法要求变换的场景,而命令模式适用于解耦两个有紧耦合关系的对象场合或者多命令多撤销的场景。 - -#2.状态模式VS策略模式 -       策略模式封装的是不同的算法,算法之间没有交互、以达到算法可以自由切换的目的;而状态模式封装的是不同的状态,以达到状态切换行为随之发生改变的目的。这两个模式虽然都有变换的行为,但是两者的目标却是不同的。 - -##2.1最佳实践 - * 环境角色的职责不同 -       两者都有一个叫做Context环境角色的类, 策略模式的环境角色只是一个委托作用,负责算法的替换;而状态模式的环境角色不仅仅是委托行为,它还具有登记状态变化的功能,与具体的状态类协作,共同完成状态切换的任务。 - * 解决问题的重点不同 -       策略模式旨在解决内部算法如何改变的问题,也就是将内部算法的改变对外界的影响降低到最小程度,它保证算法可以自由切换;而状态模式旨在解决内在状态的改变而引起行为改变的问题,它的出发点是事物的状态,封装状态而暴露行为,一个对象的状态改变,从外界看就好像是行为改变。 - * 解决问题的方法不同 -       策略模式只是确保算法可以自由切换,但是什么时候用什么算法它决定不了;而状态模式对外暴露的是行为,状态的变化一般由环境角色和具体状态共同完成的,也就是说状态模式封装了状态的变化而暴露了不同的行为或行为结果。 - * 应用场景不同 -       策略模式是一系列平行的、可相互替换的算法封装后的结构。状态模式则要求有一系列状态发生变化的场景,它要求的是有状态且有行为的场景,也就是一个对象必须具有二维描述才能采用状态模式,如果只有状态而没有行为,则状态的变化就失去了意义。 - * 复杂度不同 -       通常策略模式结构简单,扩展比较容易,而且代码也容易阅读。而状态模式通常比较复杂,因为它要从两个角色看到一个对象状态和行为的改变,也就是说它封装的是变化,要知道变化是无穷尽的,因此相对来说状态模式通常都比较复杂,涉及面很多,虽然也很容易扩展,但是一般不会进行大规模的扩展和修正。 - -#3.观察者模式VS责任链模式 -       因为观察者模式也提到了触发链的问题,所以把这两个模式放到一起对比。 - -##3.1最佳实践 - * 链中消息对象不同 -       从首节点开始到最终的尾节点,两个链中传递的消息对象是不同的。责任链模式基本上不改变消息对象的结构,但是在触发链传递的对象可以自由变化,只要上下级节点对传递对象了解即可。 - * 上下节点的关系不同 -       在责任链中,上下节点没有关系,都是接收同样的对象。而触发链模式的上下级关系很亲密,链中的任意两个相邻节点都是一个牢固的独立团体。 - * 消息的分销渠道不同 -       在责任链模式中,一个消息从链首传递进来后,就开始沿着链条向链尾运动,方向是单一的,固定的;而触发链模式则不同,由于它采用的是观察者模式,所以有非常大的灵活性,一个消息传递到链首后,具体怎么传递是不固定的,可以以广播方式传递,也可以以跳跃方式传递,这取决于处理消息的逻辑。 +# 行为类模式大PK +---------- + +# 1.命令模式VS策略模式 +       策略模式的意图是封装算法,它认为算法已经是一个完整的、不可拆分的原子业务(注意这里是原子业务,而不是原子对象),即其意图是让这些算法独立,并且可以相互替换,让行为的变化独立于拥有行为的客户;而命令模式则是对动作的解耦,把一个动作的执行分为执行对象(接收者角色)、执行行为(命令角色),让两者相互独立而不相互影响。 + +### 1.1最佳实践 +       命令模式和策略模式的类图完全一样,代码实现也比较类似,但是两者还是有区别的。 + +- 关注点不同 +       策略模式关注的是算法的完整性、封装性,只有具备了这两个条件才能保证其可以自由切换。命令模式则关注的是解耦问题。 +- 角色功能不同 +       策略模式中的抽象算法和具体算法与命令模式的接收者非常相似,但是他们的职责不同。策略模式中的具体算法是负责一个完整算法逻辑,它是不可再拆分的原子业务单元,一旦变更就是对算法整体的变更。而命令模式关注的是功能的实现。 +- 使用场景不同 +       策略模式使用于算法要求变换的场景,而命令模式适用于解耦两个有紧耦合关系的对象场合或者多命令多撤销的场景。 + +# 2.状态模式VS策略模式 +       策略模式封装的是不同的算法,算法之间没有交互、以达到算法可以自由切换的目的;而状态模式封装的是不同的状态,以达到状态切换行为随之发生改变的目的。这两个模式虽然都有变换的行为,但是两者的目标却是不同的。 + +### 2.1最佳实践 + * 环境角色的职责不同 +       两者都有一个叫做Context环境角色的类, 策略模式的环境角色只是一个委托作用,负责算法的替换;而状态模式的环境角色不仅仅是委托行为,它还具有登记状态变化的功能,与具体的状态类协作,共同完成状态切换的任务。 + * 解决问题的重点不同 +       策略模式旨在解决内部算法如何改变的问题,也就是将内部算法的改变对外界的影响降低到最小程度,它保证算法可以自由切换;而状态模式旨在解决内在状态的改变而引起行为改变的问题,它的出发点是事物的状态,封装状态而暴露行为,一个对象的状态改变,从外界看就好像是行为改变。 + * 解决问题的方法不同 +       策略模式只是确保算法可以自由切换,但是什么时候用什么算法它决定不了;而状态模式对外暴露的是行为,状态的变化一般由环境角色和具体状态共同完成的,也就是说状态模式封装了状态的变化而暴露了不同的行为或行为结果。 + * 应用场景不同 +       策略模式是一系列平行的、可相互替换的算法封装后的结构。状态模式则要求有一系列状态发生变化的场景,它要求的是有状态且有行为的场景,也就是一个对象必须具有二维描述才能采用状态模式,如果只有状态而没有行为,则状态的变化就失去了意义。 + * 复杂度不同 +       通常策略模式结构简单,扩展比较容易,而且代码也容易阅读。而状态模式通常比较复杂,因为它要从两个角色看到一个对象状态和行为的改变,也就是说它封装的是变化,要知道变化是无穷尽的,因此相对来说状态模式通常都比较复杂,涉及面很多,虽然也很容易扩展,但是一般不会进行大规模的扩展和修正。 + +# 3.观察者模式VS责任链模式 +       因为观察者模式也提到了触发链的问题,所以把这两个模式放到一起对比。 + +### 3.1最佳实践 + * 链中消息对象不同 +       从首节点开始到最终的尾节点,两个链中传递的消息对象是不同的。责任链模式基本上不改变消息对象的结构,但是在触发链传递的对象可以自由变化,只要上下级节点对传递对象了解即可。 + * 上下节点的关系不同 +       在责任链中,上下节点没有关系,都是接收同样的对象。而触发链模式的上下级关系很亲密,链中的任意两个相邻节点都是一个牢固的独立团体。 + * 消息的分销渠道不同 +       在责任链模式中,一个消息从链首传递进来后,就开始沿着链条向链尾运动,方向是单一的,固定的;而触发链模式则不同,由于它采用的是观察者模式,所以有非常大的灵活性,一个消息传递到链首后,具体怎么传递是不固定的,可以以广播方式传递,也可以以跳跃方式传递,这取决于处理消息的逻辑。 diff --git a/src/dp/com/company/pkcreate/README.md b/src/dp/com/company/pkcreate/README.md index a33afb6..b8db490 100644 --- a/src/dp/com/company/pkcreate/README.md +++ b/src/dp/com/company/pkcreate/README.md @@ -1,20 +1,20 @@ -#1.工厂方法模式VS建造者模式 -      工厂方法模式注重的是整体对象的创建方法,而建造者模式注重的是部件构建的过程,旨在通过一步步的精确构造创建出一个复杂的对象。 - - -##1.1最佳实践 -      工厂方法模式和建造者模式都属于对象创建类模式,都用来创建类的对象。但它们之间的区别还是比较明显的: - -- 意图不同 -      在工厂方法模式里,我们关注的是一个产品整体,例如超人整体,无须关心产品的各部分是如何创建出来的;但在建造者模式中,一个具体产品的产生是依赖各个部件的产生以及装配顺序。简单地说,工厂模式是一个对象创建的粗线条应用,建造者模式则是通过细线条勾勒出一个复杂对象,关注的产品组成部分的创建过程。 -- 产品的复杂度不同 -      工厂方法模式创建的产品一般都是单一性质的产品,而建造者模式创建的则是一个复合产品。工厂方法模式的对象粒度比较粗,建造者模式的产品对象粒度比较细。 - - -#2.抽象工厂模式VS建造者模式 -      抽象工厂模式实现对产品家族的创建,不需要关心构建过程,只关心什么产品由什么工厂生产即可。 - - -##2.1最佳实践 -      抽象工厂模式中使用“工厂”来描述构建者,就好比一个一个的工厂,它是从一个更高层次去看对象的构建,具体到工厂内部还有很多的车间。而在建造者模式中使用“车间”来描述构建者。不同的车间完成不同的创建和装配任务,它更关心过程。 -      抽象工厂模式比建造者模式的尺度要大,它关注产品整体,而建造者模式关注构建过程,因此建造者模式可以很容易地创建出一个崭新的产品,只要导演类能够提供具体的工艺流程。也正因为如此,两者的应用场景截然不同,如果希望屏蔽对象的创建过程,只提供一个封装良好的对象,则可以选择抽象工厂方法模式。而创建者模式可以用在构建的装配方面,如通过装配不同的组件或者相同组件的不同顺序,可以产生出一个新的对象,它可以产生一个非常灵活的架构,方便地扩展和维护系统。 +# 1.工厂方法模式VS建造者模式 +      工厂方法模式注重的是整体对象的创建方法,而建造者模式注重的是部件构建的过程,旨在通过一步步的精确构造创建出一个复杂的对象。 + + +## 1.1最佳实践 +      工厂方法模式和建造者模式都属于对象创建类模式,都用来创建类的对象。但它们之间的区别还是比较明显的: + +- 意图不同 +      在工厂方法模式里,我们关注的是一个产品整体,例如超人整体,无须关心产品的各部分是如何创建出来的;但在建造者模式中,一个具体产品的产生是依赖各个部件的产生以及装配顺序。简单地说,工厂模式是一个对象创建的粗线条应用,建造者模式则是通过细线条勾勒出一个复杂对象,关注的产品组成部分的创建过程。 +- 产品的复杂度不同 +      工厂方法模式创建的产品一般都是单一性质的产品,而建造者模式创建的则是一个复合产品。工厂方法模式的对象粒度比较粗,建造者模式的产品对象粒度比较细。 + + +# 2.抽象工厂模式VS建造者模式 +      抽象工厂模式实现对产品家族的创建,不需要关心构建过程,只关心什么产品由什么工厂生产即可。 + + +## 2.1最佳实践 +      抽象工厂模式中使用“工厂”来描述构建者,就好比一个一个的工厂,它是从一个更高层次去看对象的构建,具体到工厂内部还有很多的车间。而在建造者模式中使用“车间”来描述构建者。不同的车间完成不同的创建和装配任务,它更关心过程。 +      抽象工厂模式比建造者模式的尺度要大,它关注产品整体,而建造者模式关注构建过程,因此建造者模式可以很容易地创建出一个崭新的产品,只要导演类能够提供具体的工艺流程。也正因为如此,两者的应用场景截然不同,如果希望屏蔽对象的创建过程,只提供一个封装良好的对象,则可以选择抽象工厂方法模式。而创建者模式可以用在构建的装配方面,如通过装配不同的组件或者相同组件的不同顺序,可以产生出一个新的对象,它可以产生一个非常灵活的架构,方便地扩展和维护系统。 diff --git a/src/dp/com/company/pkstructure/README.md b/src/dp/com/company/pkstructure/README.md index b8bd695..4d70e72 100644 --- a/src/dp/com/company/pkstructure/README.md +++ b/src/dp/com/company/pkstructure/README.md @@ -1,23 +1,23 @@ -#1.代理模式VS装饰模式 -      装饰模式就是代理模式的一个特殊应用,两者的共同点是都具有相同的接口,不同点则是代理模式着重对代理过程的控制,而装饰模式则是对类的功能进行加强或减弱,它着重类的功能变化。 - -##1.1最佳实践 -      代理模式是把当前的行为或功能委托给其他对象执行,代理类负责接口限定:是否可以调用真实角色,以及是否对发送到真实角色的消息进行变形处理,它不对被主题角色(也就是被代理类)的功能做任何处理,保证原汁原味的调用。代理模式使用到极致开发就是AOP。 -      装饰模式是在要保证接口不变的情况下加强类的功能,它保证的是被修饰的对象功能比原始对象丰富(当然,也可以减弱),但不做准入条件判断和准入参数过滤,如是否可以执行类的功能,过滤输入参数是否合规等,这不是装饰模式关心的。 - - -#2.适配器模式VS装饰模式 -      适配器模式和装饰模式在通用类图上没有太多的相似点,差别比较大,但是它们有相似的地方:都是包装作用,都是通过委托方式实现其功能。不同点:装饰模式包装的是自己的兄弟类,隶属于同一个家族,适配器则修饰非血缘关系类,把一个非本家族的对象伪装成本家族的对象,注意是伪装,因此它的本质还是非相同接口的对象。 - - -##2.1最佳实践 -      我们发现这两个模式有较多的不同点。 - -- 意图不同 -      装饰模式的意图是加强对象的功能;而适配器模式关注的则是转化,它的主要意图是两个不同对象之间的转化。 -- 施与对象不同 -      装饰模式装饰的对象必须是自己的同宗,也就是相同的接口或父类,只要在具有相同的属性和行为的情况下,才能比较行为是增加还是减弱;适配器模式则必须是两个不同的对象,因为它着重与转换,只有两个不同的对象才能有转换的必要。 -- 场景不同 -      装饰模式在任何时候都可以用,只要是想增强类的功能,而适配器模式则是一个补救模式,一般出现在系统成熟或已经构建完毕的项目中,作为一个紧急处理手段采用。 -- 扩展性不同 -      装饰模式很容易扩展。 +# 1.代理模式VS装饰模式 +      装饰模式就是代理模式的一个特殊应用,两者的共同点是都具有相同的接口,不同点则是代理模式着重对代理过程的控制,而装饰模式则是对类的功能进行加强或减弱,它着重类的功能变化。 + +## 1.1最佳实践 +      代理模式是把当前的行为或功能委托给其他对象执行,代理类负责接口限定:是否可以调用真实角色,以及是否对发送到真实角色的消息进行变形处理,它不对被主题角色(也就是被代理类)的功能做任何处理,保证原汁原味的调用。代理模式使用到极致开发就是AOP。 +      装饰模式是在要保证接口不变的情况下加强类的功能,它保证的是被修饰的对象功能比原始对象丰富(当然,也可以减弱),但不做准入条件判断和准入参数过滤,如是否可以执行类的功能,过滤输入参数是否合规等,这不是装饰模式关心的。 + + +# 2.适配器模式VS装饰模式 +      适配器模式和装饰模式在通用类图上没有太多的相似点,差别比较大,但是它们有相似的地方:都是包装作用,都是通过委托方式实现其功能。不同点:装饰模式包装的是自己的兄弟类,隶属于同一个家族,适配器则修饰非血缘关系类,把一个非本家族的对象伪装成本家族的对象,注意是伪装,因此它的本质还是非相同接口的对象。 + + +## 2.1最佳实践 +      我们发现这两个模式有较多的不同点。 + +- 意图不同 +      装饰模式的意图是加强对象的功能;而适配器模式关注的则是转化,它的主要意图是两个不同对象之间的转化。 +- 施与对象不同 +      装饰模式装饰的对象必须是自己的同宗,也就是相同的接口或父类,只要在具有相同的属性和行为的情况下,才能比较行为是增加还是减弱;适配器模式则必须是两个不同的对象,因为它着重与转换,只有两个不同的对象才能有转换的必要。 +- 场景不同 +      装饰模式在任何时候都可以用,只要是想增强类的功能,而适配器模式则是一个补救模式,一般出现在系统成熟或已经构建完毕的项目中,作为一个紧急处理手段采用。 +- 扩展性不同 +      装饰模式很容易扩展。 diff --git a/src/dp/com/company/prototype/README.md b/src/dp/com/company/prototype/README.md index 9d2857c..b0d8eb1 100644 --- a/src/dp/com/company/prototype/README.md +++ b/src/dp/com/company/prototype/README.md @@ -1,27 +1,27 @@ -#原型模式(Proxy Pattern) -定义:Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype. 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 - -原型模式的通用类图如下图: -![Alt text](prototype.gif "原型模式类图") - -原型模式的核心是一个clone方法,通过该方法进行对象的拷贝,Java提供了一个Cloneable接口来标示这个对象是可拷贝的,为什么说是“标示”呢?翻开JDK的帮助看看Cloneable是一个方法都没有的,这个接口只是一个标记作用,在JVM中具有这个标记的对象才有可能被拷贝,那怎么才能从“有可能被拷贝”转换为“可以被拷贝”呢?方法是覆盖clone()方法。实现一个接口,然后重写clone方法,就完成了原型模式! - - -#原型模式的应用 -##原型模式的优点 - * 性能优良。原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好的体现其优点。 - * 逃避构造函数的约束。这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的(见“原型模式的注意事项”),优点就是减少了约束,缺点也是减少了约束,双刃剑,需要大家在实际应用时考虑。 - - -##原型模式的使用场景 - * 资源优化场景。类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。 - * 性能和安全要求的场景。通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。 - * 一个对象多个修改者的场景。一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。 - -在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与Java融为浑然一体,大家可以随手拿来使用。 - - -#原型模式的注意事项 - * 构造函数不会被执行。 - * 浅拷贝和深拷贝。Object类提供的方法clone只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做浅拷贝,确实是非常浅,两个对象共享了一个私有变量,你改我改大家都能改,是一种非常不安全的方式,在实际项目中使用还是比较少的(当然,这是也是一种“危机”环境的一种救命方式)。内部的数组和引用对象才不拷贝,其他的原始类型比如int,long,String(Java就希望你把String认为是基本类型,String是没有clone方法的)等都会被拷贝的。(注意:使用clone方法拷贝时,满足两个条件的对象才不会被拷贝:一是类的成员变量,而不是方法内的变量;二是必须是一个对象,而不是一个原始类型) +# 原型模式(Proxy Pattern) +定义:Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype. 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 + +原型模式的通用类图如下图: +![Alt text](prototype.gif "原型模式类图") + +原型模式的核心是一个clone方法,通过该方法进行对象的拷贝,Java提供了一个Cloneable接口来标示这个对象是可拷贝的,为什么说是“标示”呢?翻开JDK的帮助看看Cloneable是一个方法都没有的,这个接口只是一个标记作用,在JVM中具有这个标记的对象才有可能被拷贝,那怎么才能从“有可能被拷贝”转换为“可以被拷贝”呢?方法是覆盖clone()方法。实现一个接口,然后重写clone方法,就完成了原型模式! + + +# 原型模式的应用 +### 1.原型模式的优点 + * 性能优良。原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好的体现其优点。 + * 逃避构造函数的约束。这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的(见“原型模式的注意事项”),优点就是减少了约束,缺点也是减少了约束,双刃剑,需要大家在实际应用时考虑。 + + +### 2.原型模式的使用场景 + * 资源优化场景。类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。 + * 性能和安全要求的场景。通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。 + * 一个对象多个修改者的场景。一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。 + +在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与Java融为浑然一体,大家可以随手拿来使用。 + + +# 原型模式的注意事项 + * 构造函数不会被执行。 + * 浅拷贝和深拷贝。Object类提供的方法clone只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做浅拷贝,确实是非常浅,两个对象共享了一个私有变量,你改我改大家都能改,是一种非常不安全的方式,在实际项目中使用还是比较少的(当然,这是也是一种“危机”环境的一种救命方式)。内部的数组和引用对象才不拷贝,其他的原始类型比如int,long,String(Java就希望你把String认为是基本类型,String是没有clone方法的)等都会被拷贝的。(注意:使用clone方法拷贝时,满足两个条件的对象才不会被拷贝:一是类的成员变量,而不是方法内的变量;二是必须是一个对象,而不是一个原始类型) * clone与final两对冤家。对象的clone与对象内的final关键字是有冲突的。要使用clone方法,类的成员变量上不要增加final关键字。 \ No newline at end of file diff --git a/src/dp/com/company/proxy/README.md b/src/dp/com/company/proxy/README.md index 4305094..196f058 100644 --- a/src/dp/com/company/proxy/README.md +++ b/src/dp/com/company/proxy/README.md @@ -1,56 +1,56 @@ -#代理模式(Proxy Pattern) -定义:provide a surrogate or placeholder for another object to control access to it.为其他对象提供一种代理以控制对这个对象的访问。 - -代理模式的通用类图如下图: -![Alt text](proxy.jpg "代理模式类图") - -代理模式也叫做委托模式,它是一项基本设计技巧,许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式,而且在日常的应用中,代理模式可以提供非常好的访问控制,在一些著名开源软件中也经常见到它的身影,如Struts2的Form元素映射就采用了代理模式(准确的说是动态代理模式)。我们先看一下类图中的三个角色的定义: - -- Subject抽象主题角色。抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求。 -- RealSubject 具体主题角色。也叫做被委托角色、被代理角色,它才是冤大头,是业务逻辑的具体执行者。 -- Proxy代理主题角色。也叫做委托类、代理类,它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。 - -#代理模式的应用 -##代理模式的优点 - * 职责清晰。真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。 - * 高扩展性。具体主题角色是随时都会发生变化的,只要它实现了接口,甭管它如何变化,都逃不脱如来佛的手掌(接口),那我们的代理类完全就可以在不做任何修改的情况下使用。 - * 智能化。这在我们以上讲解中还没有体现出来,不过在我们以下的动态代理章节中你就会看到代理的智能化,读者有兴趣也可以看看Struts是如何把表单元素映射到对象上的。 - -##代理模式的应用 -我相信第一次接触到代理模式的读者肯定很郁闷,为什么要用代理呀,是的,为什么要用代理?想想现实世界吧,你为什么要找代理律师,你去打官司,为什么要找个律师?因为你不想参与中间过程的是是非非,只要完成自己的答辩就成,其他的比如事前调查、事后追查都由律师来搞定,这就是为了减轻你的负担。代理模式使用非常多,大家可以看看Spring AOP,这是一个非常典型的动态代理。 - - -#代理模式的扩展 -##普通代理 - 在网络上代理服务器设置分为透明代理和普通代理,是什么意思呢?透明代理就是用户不用设置代理服务器地址,就可以直接访问,也就是说代理服务器对用户来说透明的,看不到,不用知道它存在的;普通代理则是需要用户自己设置代理服务器的IP地址,用户必须知道代理的存在。我们设计模式中的普通代理和强制代理也是类似的一种结构,普通代理就是我们要知道代理的存在,也就是类似的GamePlayerProxy这个类的存在,然后才能访问;强制代理则是调用者直接调用真实角色,而不用关心代理是否存在,其代理的产生是由真实角色决定的,这样解释还是比较复杂,我们还是用实例来讲解。 - -首先说普通代理,它的要求就是客户端只能访问代理角色,而不能访问真实角色。在该模式下,调用者只知代理而不用知道真实的角色是谁,屏蔽了真实角色的变更对高层模块的影响,真实的主题角色爱怎么修改就怎么修改,对高层次的模块没有任何的影响,只要你实现了接口所对应的方法,该模式非常适合对扩展性要求较高的场合。当然,在实际的项目中,一般都是通过约定来禁止new一个真实的角色,也是一个非常好的方案。 - - 注意:普通代理模式的约束问题,尽量通过团队内的编程规范类约束,因为每一个主题类是可被重用的和可维护的,使用技术约束的方式对系统维护是一种非常不利的因素。 - - -##强制代理 -强制代理在设计模式中比较另类,为什么这么说呢?一般的思维都是通过代理找到真实的角色,但是强制代理却是要“强制”,你必须通过真实角色查找到代理角色,否则你不能访问,甭管你是通过代理类还是通过直接new一个主题角色类,都不能访问,只有通过真实角色指定的代理类才可以访问,也就是说由真实角色管理代理角色,这么说吧,高层模块new了一个真实角色的对象,返回的却是代理角色。 - -强制代理的概念就是要从真实角色查找到代理角色,不允许直接访问真实角色,高层模块只要调用getProxy就可以访问真实角色的所有方法,它根本就不需要产生一个代理出来,代理的管理已经由真实角色自己完成。 - - -##代理是有个性的 -一个类可以实现多个接口,完成不同任务的整合,那也就是说代理类不仅仅可以实现主题接口,也可以实现其他接口完成不同的任务,而且代理的目的是在目标对象方法的基础上作增强,这种增强的本质通常就是对目标对象的方法进行拦截和过滤。例如游戏代理是需要收费的,升一级需要5元钱,这个计算功能就是代理类的个性,它应该在代理的接口中定义。 - - -##虚拟代理 -虚拟代理(Virual Proxy)只要我们把代理模式的通用代码稍微修改一下就成为虚拟代理。在需要的时候才初始化主题对象,可以避免被代理对象较多而引起的初始化缓慢的问题,它的缺点就是需要在每个方法中判断主题对象是否被创建,这就是虚拟代理,非常简单。 - - -##动态代理 -动态代理模式的通用类图如下图: -![Alt text](dynamicproxy.jpg "动态代理模式类图") - -动态代理是在实现阶段不用关心代理谁,而在运行阶段才指定代理那一个对象,相对的来说,自己写代理类的方式就是静态代理。现在有一个非常流行的名称叫做:面向横切面编程,也就是AOP(Aspect Oriented Programming),其核心就是采用了动态代理机制。 - -在类图中有一个InvocationHanlder接口,是JDK提供的动态代理接口,对被代理类的方法进行代理。其中invoke方法是接口InvocationHandler定义必须实现的,它完成对真实方法的调用。我们来详细讲解一下InvocationHanlder接口,动态代理是根据被代理的接口生成所有的方法,也就是说给定一个接口,动态代理会宣称“我已经实现该接口下的所有方法了”,那各位读者想想看,动态代理怎么才能实现被代理接口中的方法呢?默认情况下所有的方法返回值都是空的,是的,代理已经实现它了,但是没有任何的逻辑含义,那怎么办?好办,通过InvocationHandler接口,所有方法都由该Handler来进行处理,即所有被代理的方法都由InvocationHandler接管实际的处理任务。 - - 注意:要实现动态代理的首要条件是:被代理类必须实现一个接口。当然了,现在也有很多技术如CGLIB可以实现不需要接口也可以实现动态代理的方式。 - +# 代理模式(Proxy Pattern) +定义:provide a surrogate or placeholder for another object to control access to it.为其他对象提供一种代理以控制对这个对象的访问。 + +代理模式的通用类图如下图: +![Alt text](proxy.jpg "代理模式类图") + +代理模式也叫做委托模式,它是一项基本设计技巧,许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式,而且在日常的应用中,代理模式可以提供非常好的访问控制,在一些著名开源软件中也经常见到它的身影,如Struts2的Form元素映射就采用了代理模式(准确的说是动态代理模式)。我们先看一下类图中的三个角色的定义: + +- Subject抽象主题角色。抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求。 +- RealSubject 具体主题角色。也叫做被委托角色、被代理角色,它才是冤大头,是业务逻辑的具体执行者。 +- Proxy代理主题角色。也叫做委托类、代理类,它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。 + +# 代理模式的应用 +### 1.代理模式的优点 + * 职责清晰。真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。 + * 高扩展性。具体主题角色是随时都会发生变化的,只要它实现了接口,甭管它如何变化,都逃不脱如来佛的手掌(接口),那我们的代理类完全就可以在不做任何修改的情况下使用。 + * 智能化。这在我们以上讲解中还没有体现出来,不过在我们以下的动态代理章节中你就会看到代理的智能化,读者有兴趣也可以看看Struts是如何把表单元素映射到对象上的。 + +### 2.代理模式的应用 +我相信第一次接触到代理模式的读者肯定很郁闷,为什么要用代理呀,是的,为什么要用代理?想想现实世界吧,你为什么要找代理律师,你去打官司,为什么要找个律师?因为你不想参与中间过程的是是非非,只要完成自己的答辩就成,其他的比如事前调查、事后追查都由律师来搞定,这就是为了减轻你的负担。代理模式使用非常多,大家可以看看Spring AOP,这是一个非常典型的动态代理。 + + +# 代理模式的扩展 +### 1.普通代理 + 在网络上代理服务器设置分为透明代理和普通代理,是什么意思呢?透明代理就是用户不用设置代理服务器地址,就可以直接访问,也就是说代理服务器对用户来说透明的,看不到,不用知道它存在的;普通代理则是需要用户自己设置代理服务器的IP地址,用户必须知道代理的存在。我们设计模式中的普通代理和强制代理也是类似的一种结构,普通代理就是我们要知道代理的存在,也就是类似的GamePlayerProxy这个类的存在,然后才能访问;强制代理则是调用者直接调用真实角色,而不用关心代理是否存在,其代理的产生是由真实角色决定的,这样解释还是比较复杂,我们还是用实例来讲解。 + +首先说普通代理,它的要求就是客户端只能访问代理角色,而不能访问真实角色。在该模式下,调用者只知代理而不用知道真实的角色是谁,屏蔽了真实角色的变更对高层模块的影响,真实的主题角色爱怎么修改就怎么修改,对高层次的模块没有任何的影响,只要你实现了接口所对应的方法,该模式非常适合对扩展性要求较高的场合。当然,在实际的项目中,一般都是通过约定来禁止new一个真实的角色,也是一个非常好的方案。 + + > 注意:普通代理模式的约束问题,尽量通过团队内的编程规范类约束,因为每一个主题类是可被重用的和可维护的,使用技术约束的方式对系统维护是一种非常不利的因素。 + + +### 2.强制代理 +强制代理在设计模式中比较另类,为什么这么说呢?一般的思维都是通过代理找到真实的角色,但是强制代理却是要“强制”,你必须通过真实角色查找到代理角色,否则你不能访问,甭管你是通过代理类还是通过直接new一个主题角色类,都不能访问,只有通过真实角色指定的代理类才可以访问,也就是说由真实角色管理代理角色,这么说吧,高层模块new了一个真实角色的对象,返回的却是代理角色。 + +强制代理的概念就是要从真实角色查找到代理角色,不允许直接访问真实角色,高层模块只要调用getProxy就可以访问真实角色的所有方法,它根本就不需要产生一个代理出来,代理的管理已经由真实角色自己完成。 + + +### 3.代理是有个性的 +一个类可以实现多个接口,完成不同任务的整合,那也就是说代理类不仅仅可以实现主题接口,也可以实现其他接口完成不同的任务,而且代理的目的是在目标对象方法的基础上作增强,这种增强的本质通常就是对目标对象的方法进行拦截和过滤。例如游戏代理是需要收费的,升一级需要5元钱,这个计算功能就是代理类的个性,它应该在代理的接口中定义。 + + +### 4.虚拟代理 +虚拟代理(Virual Proxy)只要我们把代理模式的通用代码稍微修改一下就成为虚拟代理。在需要的时候才初始化主题对象,可以避免被代理对象较多而引起的初始化缓慢的问题,它的缺点就是需要在每个方法中判断主题对象是否被创建,这就是虚拟代理,非常简单。 + + +### 5.动态代理 +动态代理模式的通用类图如下图: +![Alt text](dynamicproxy.jpg "动态代理模式类图") + +动态代理是在实现阶段不用关心代理谁,而在运行阶段才指定代理那一个对象,相对的来说,自己写代理类的方式就是静态代理。现在有一个非常流行的名称叫做:面向横切面编程,也就是AOP(Aspect Oriented Programming),其核心就是采用了动态代理机制。 + +在类图中有一个InvocationHanlder接口,是JDK提供的动态代理接口,对被代理类的方法进行代理。其中invoke方法是接口InvocationHandler定义必须实现的,它完成对真实方法的调用。我们来详细讲解一下InvocationHanlder接口,动态代理是根据被代理的接口生成所有的方法,也就是说给定一个接口,动态代理会宣称“我已经实现该接口下的所有方法了”,那各位读者想想看,动态代理怎么才能实现被代理接口中的方法呢?默认情况下所有的方法返回值都是空的,是的,代理已经实现它了,但是没有任何的逻辑含义,那怎么办?好办,通过InvocationHandler接口,所有方法都由该Handler来进行处理,即所有被代理的方法都由InvocationHandler接管实际的处理任务。 + + > 注意:要实现动态代理的首要条件是:被代理类必须实现一个接口。当然了,现在也有很多技术如CGLIB可以实现不需要接口也可以实现动态代理的方式。 + 再次说明,以上的动态代理是一个通用代理框架,如果你想设计自己的AOP框架,完全可以在此基础上扩展,我们设计的是一个通用代理,只要有一个接口,一个实现类,就可以使用该代理,完成代理的所有功效。 \ No newline at end of file diff --git a/src/dp/com/company/singleton/README.md b/src/dp/com/company/singleton/README.md index 06a0806..bf237fa 100644 --- a/src/dp/com/company/singleton/README.md +++ b/src/dp/com/company/singleton/README.md @@ -1,40 +1,40 @@ -#单例模式(Singleton Pattern) -定义:Ensure a class has only one instance, and provide a global point of access to it. 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。 -Singleton类称为单例类,通过使用private的构造函数,确保了在一个应用中只产生一个实例,并且是自行实例化的(在Singleton中自己使用new Singleton())。 - -单例模式的通用类图如下图: -![Alt text](singleton.gif "单例模式类图") - - -#单例模式应用 -##1.单例模式的优点 - * 由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁的被创建、销毁,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显; - * 由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决(在Java EE中采用单例模式时需要注意JVM垃圾回收机制); - * 单例模式可以避免对资源的多重占用,例如一个写文件动作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作。 - * 单例模式可以在系统设置全局的访问点,优化环共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。 - - -##2.单例模式的缺点 - * 单例模式没有接口,扩展很困难,若要扩展,除了修改代码没有第二种途径可以实现。单例模式为什么不能增加接口呢?因为接口对单例模式是没有任何的意义,它要求“自行实例化”,并且提供单一实例、接口或抽象类是不可能被实例化的。 - * 单例模式对测试是不利的。在并行开发环境中,如果单例模式没有完成,是不能进行测试的,没有接口也不能使用mock的方式虚拟一个对象。 - * 单例模式与单一职责原则有冲突。一个类应该只实现一个的逻辑,而不关心它是否是单例的,决定它是不是要单例是环境决定的,单例模式把“要单例”和业务逻辑融合也在一个类中。 - - -##3.单例模式的使用场景 - * 要求生成唯一序列号的环境; - * 在整个项目中需要有访问一个共享访问点或共享数据,例如一个Web页面上的计数器,可以不用每次刷新都记录到数据库中,使用单例模式保持计数器的值,并确保是线程安全的; - * 创建一个对象需要消耗的资源过多,如要访问IO、访问数据库等资源; - * 需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式(当然,也可以直接声明为static的方式); - - -#单例模式的扩展 -需要产生固定数量对象的模式就叫做有上限的多例模式,它是单例模式的一种扩展,采用有上限的多例模式,我们可以在设计时决定在内存中有多少个实例,方便系统进行扩展,修正单例可能存在的性能问题,提供系统的响应速度。例如读取文件,我们可以在系统启动时完成初始化工作,在内存中启动固定数量的reader实例,然后在需要读取文件时就可以快速响应。 - - -#最佳实践 -单例模式是23个模式中比较简单的模式,应用也非常广泛,如在Spring中,每个Bean默认就是单例的,这样做的优点是Spring容器可以管理这些Bean的生命期,决定什么时候创建出来,什么时候销毁,销毁的时候要如何处理,等等。如果采用非单例模式(Prototype类型),则Bean初始化后的管理则交由J2EE容器,Spring容器不再跟踪管理Bean的生命周期。 -使用单例模式需要注意的一点就是JVM的垃圾回收机制,如果我们的一个单例对象在内存中长久不使用,JVM就认为这个对象是一个垃圾,在CPU资源空闲的情况下该对象会被清理掉,下次再调用时就需要重新产生一个对象。如果我们在应用中使用单例类作为有状态值(如计数器)的管理,则会出现回复原状的情况,应用就会出现故障。如果确实需要采用单例模式来记录有状态的值,有两种办法可以解决该问题: - - -- 由容器管理单例的生命周期 +# 单例模式(Singleton Pattern) +定义:Ensure a class has only one instance, and provide a global point of access to it. 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。 +Singleton类称为单例类,通过使用private的构造函数,确保了在一个应用中只产生一个实例,并且是自行实例化的(在Singleton中自己使用new Singleton())。 + +单例模式的通用类图如下图: +![Alt text](singleton.gif "单例模式类图") + + +# 单例模式应用 +### 1.单例模式的优点 + * 由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁的被创建、销毁,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显; + * 由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决(在Java EE中采用单例模式时需要注意JVM垃圾回收机制); + * 单例模式可以避免对资源的多重占用,例如一个写文件动作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作。 + * 单例模式可以在系统设置全局的访问点,优化环共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。 + + +### 2.单例模式的缺点 + * 单例模式没有接口,扩展很困难,若要扩展,除了修改代码没有第二种途径可以实现。单例模式为什么不能增加接口呢?因为接口对单例模式是没有任何的意义,它要求“自行实例化”,并且提供单一实例、接口或抽象类是不可能被实例化的。 + * 单例模式对测试是不利的。在并行开发环境中,如果单例模式没有完成,是不能进行测试的,没有接口也不能使用mock的方式虚拟一个对象。 + * 单例模式与单一职责原则有冲突。一个类应该只实现一个的逻辑,而不关心它是否是单例的,决定它是不是要单例是环境决定的,单例模式把“要单例”和业务逻辑融合也在一个类中。 + + +### 3.单例模式的使用场景 + * 要求生成唯一序列号的环境; + * 在整个项目中需要有访问一个共享访问点或共享数据,例如一个Web页面上的计数器,可以不用每次刷新都记录到数据库中,使用单例模式保持计数器的值,并确保是线程安全的; + * 创建一个对象需要消耗的资源过多,如要访问IO、访问数据库等资源; + * 需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式(当然,也可以直接声明为static的方式); + + +# 单例模式的扩展 +需要产生固定数量对象的模式就叫做有上限的多例模式,它是单例模式的一种扩展,采用有上限的多例模式,我们可以在设计时决定在内存中有多少个实例,方便系统进行扩展,修正单例可能存在的性能问题,提供系统的响应速度。例如读取文件,我们可以在系统启动时完成初始化工作,在内存中启动固定数量的reader实例,然后在需要读取文件时就可以快速响应。 + + +# 最佳实践 +单例模式是23个模式中比较简单的模式,应用也非常广泛,如在Spring中,每个Bean默认就是单例的,这样做的优点是Spring容器可以管理这些Bean的生命期,决定什么时候创建出来,什么时候销毁,销毁的时候要如何处理,等等。如果采用非单例模式(Prototype类型),则Bean初始化后的管理则交由J2EE容器,Spring容器不再跟踪管理Bean的生命周期。 +使用单例模式需要注意的一点就是JVM的垃圾回收机制,如果我们的一个单例对象在内存中长久不使用,JVM就认为这个对象是一个垃圾,在CPU资源空闲的情况下该对象会被清理掉,下次再调用时就需要重新产生一个对象。如果我们在应用中使用单例类作为有状态值(如计数器)的管理,则会出现回复原状的情况,应用就会出现故障。如果确实需要采用单例模式来记录有状态的值,有两种办法可以解决该问题: + + +- 由容器管理单例的生命周期 - 状态随时记录:可以使用异步记录的方式,或者使用观察者模式,记录状态的变化,写入文件或写入数据库中,确保即使单例对象重新初始化也可以从资源环境获得销毁前的数据,避免应用数据丢失。 \ No newline at end of file diff --git a/src/dp/com/company/srp/README.md b/src/dp/com/company/srp/README.md index cb1190f..01e5ca9 100644 --- a/src/dp/com/company/srp/README.md +++ b/src/dp/com/company/srp/README.md @@ -1,8 +1,8 @@ -#单一职责原则(SRP:Single responsibility principle) -定义:一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。 - -##单一职责原则的优点: - * 可以降低类的复杂度,实现什么职责都有清晰明确的定义; - * 提高类的可读性; - * 提高系统的可维护性; +# 单一职责原则(SRP:Single responsibility principle) +定义:一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。 + +### 单一职责原则的优点: + * 可以降低类的复杂度,实现什么职责都有清晰明确的定义; + * 提高类的可读性; + * 提高系统的可维护性; * 变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,一个接口修改只对相应地实现类有影响,对其他的接口无影响,这对系统的扩展性、维护性都有非常大的帮助。 \ No newline at end of file diff --git a/src/dp/com/company/state/README.md b/src/dp/com/company/state/README.md index 3293736..08f667d 100644 --- a/src/dp/com/company/state/README.md +++ b/src/dp/com/company/state/README.md @@ -1,33 +1,33 @@ -#状态模式(State Pattern) -定义:Allow an object to alter its behavior when its internal state changes. The object will appear to change its classes.(当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。) - - - 状态模式的核心是封装,状态的变更引起了行为的变更,从外部看起来就好像这个对象对应的类发生了改变一样。状态模式的通用类图如图所示。 -![Alt text](state.jpg "状态模式类图") - - 我们先来看看状态模式中的3个角色。 - -- State抽象状态角色:接口或抽象类,负责对象状态定义,并且封装环境角色以实现状态切换。 -- ConcreteState具体状态角色:每一个具体状态必须完成两个职责:本状态的行为管理以及趋向状态处理,通俗地说,就是本状态下要做的事情,以及本状态如何过渡到其他状态。 -- Context环境角色:定义客户端需要的接口,并且负责具体状态的切换。 - - -#状态模式的应用 -##1.状态模式的优点 - * 结构清晰:避免了过多的switch...case或者if...else语句的使用,避免了程序的复杂性,提供系统的可维护性。 - * 遵循设计原则:很好地体现了开闭原则和单一职责原则,每个状态都是一个子类,你要增加状态就要增加子类,你要修改状态,你只修改一个子类就可以了。 - * 封装性非常好:这也是状态模式的基本要求,状态变换放置到类的内部来实现,外部的调用不用知道类内部如何实现状态和行为的变换。 - - -##2.状态模式的缺点 -状态模式既然有优点,那当然有缺点了。但只有一个缺点,子类会太多,也就是类膨胀。如果一个事物有很多个状态也不稀奇,如果完全使用状态模式就会有太多的子类,不好管理。 - - -##3.状态模式的使用场景 - * 行为随状态改变而改变的场景。 - * 条件、分支判断语句的替代者:在程序中大量使用switch语句或者if判断语句会导致程序结构不清晰,逻辑混乱,使用状态模式可以很好地避免这一问题,它通过扩展子类实现了条件的判断处理。 - -##4.状态模式的注意事项 -状态模式适用于当某个对象在它的状态发生改变时,它的行为也随着发生比较大的变化,也就是说在行为受状态约束的情况下可以使用状态模式,而且使用时对象的状态最好不要超过5个。 - +# 状态模式(State Pattern) +定义:Allow an object to alter its behavior when its internal state changes. The object will appear to change its classes.(当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。) + + + 状态模式的核心是封装,状态的变更引起了行为的变更,从外部看起来就好像这个对象对应的类发生了改变一样。状态模式的通用类图如图所示。 +![Alt text](state.jpg "状态模式类图") + + 我们先来看看状态模式中的3个角色。 + +- State抽象状态角色:接口或抽象类,负责对象状态定义,并且封装环境角色以实现状态切换。 +- ConcreteState具体状态角色:每一个具体状态必须完成两个职责:本状态的行为管理以及趋向状态处理,通俗地说,就是本状态下要做的事情,以及本状态如何过渡到其他状态。 +- Context环境角色:定义客户端需要的接口,并且负责具体状态的切换。 + + +# 状态模式的应用 +### 1.状态模式的优点 + * 结构清晰:避免了过多的switch...case或者if...else语句的使用,避免了程序的复杂性,提供系统的可维护性。 + * 遵循设计原则:很好地体现了开闭原则和单一职责原则,每个状态都是一个子类,你要增加状态就要增加子类,你要修改状态,你只修改一个子类就可以了。 + * 封装性非常好:这也是状态模式的基本要求,状态变换放置到类的内部来实现,外部的调用不用知道类内部如何实现状态和行为的变换。 + + +### 2.状态模式的缺点 +状态模式既然有优点,那当然有缺点了。但只有一个缺点,子类会太多,也就是类膨胀。如果一个事物有很多个状态也不稀奇,如果完全使用状态模式就会有太多的子类,不好管理。 + + +### 3.状态模式的使用场景 + * 行为随状态改变而改变的场景。 + * 条件、分支判断语句的替代者:在程序中大量使用switch语句或者if判断语句会导致程序结构不清晰,逻辑混乱,使用状态模式可以很好地避免这一问题,它通过扩展子类实现了条件的判断处理。 + +### 4.状态模式的注意事项 +状态模式适用于当某个对象在它的状态发生改变时,它的行为也随着发生比较大的变化,也就是说在行为受状态约束的情况下可以使用状态模式,而且使用时对象的状态最好不要超过5个。 + \ No newline at end of file diff --git a/src/dp/com/company/strategy/README.md b/src/dp/com/company/strategy/README.md index bf817c9..3632f40 100644 --- a/src/dp/com/company/strategy/README.md +++ b/src/dp/com/company/strategy/README.md @@ -1,31 +1,31 @@ -#策略模式(Strategy Pattern) -定义:Define a family of algorithms, encapsulate each one, and make them interchangeable.(定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。) - - -策略模式的通用类图如下图: -![Alt text](strategy.jpg "策略模式类图") - -策略模式使用的就是面向对象的继承和多态机制,非常容易理解和掌握,我们再来看策略模式的三个角色。 - -- Context角色:起承上启下封装作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。 -- Strategy抽象策略角色:策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性。 -- ConcreteStrategy具体策略角色:实现抽象策略中的操作,该类含有具体的算法。 - - -#策略模式的应用 -##1.策略模式的优点 - * 算法可以自由切换。 - * 避免使用多重条件判断。 - * 扩展性良好。 - -##2.策略模式的缺点 - * 策略类数量增多:每一个策略都是一个类,复用的可能性很小,类数量增多。 - * 所有的策略类都需要对外暴露。上层模块必须知道有哪些策略,然后才能决定使用哪一种策略,这与迪米特法则是相违背的,我只是想使用一个策略,我凭什么就要了解这个策略呢?那要你的封装类还有什么意义?这是原装策略模式的一个缺点,幸运的是,我们可以使用其他模式来修正这个缺陷,如果工厂方法模式、代理模式或享元模式。 - -##3.策略模式的使用场景 - * 多个类只有在算法或行为上稍有不同的场景。 - * 算法需要自由切换的场景。 - * 需要屏蔽算法规则的场景。 - -##4.策略模式的注意事项 +# 策略模式(Strategy Pattern) +定义:Define a family of algorithms, encapsulate each one, and make them interchangeable.(定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。) + + +策略模式的通用类图如下图: +![Alt text](strategy.jpg "策略模式类图") + +策略模式使用的就是面向对象的继承和多态机制,非常容易理解和掌握,我们再来看策略模式的三个角色。 + +- Context角色:起承上启下封装作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。 +- Strategy抽象策略角色:策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性。 +- ConcreteStrategy具体策略角色:实现抽象策略中的操作,该类含有具体的算法。 + + +# 策略模式的应用 +### 1.策略模式的优点 + * 算法可以自由切换。 + * 避免使用多重条件判断。 + * 扩展性良好。 + +### 2.策略模式的缺点 + * 策略类数量增多:每一个策略都是一个类,复用的可能性很小,类数量增多。 + * 所有的策略类都需要对外暴露。上层模块必须知道有哪些策略,然后才能决定使用哪一种策略,这与迪米特法则是相违背的,我只是想使用一个策略,我凭什么就要了解这个策略呢?那要你的封装类还有什么意义?这是原装策略模式的一个缺点,幸运的是,我们可以使用其他模式来修正这个缺陷,如果工厂方法模式、代理模式或享元模式。 + +### 3.策略模式的使用场景 + * 多个类只有在算法或行为上稍有不同的场景。 + * 算法需要自由切换的场景。 + * 需要屏蔽算法规则的场景。 + +### 4.策略模式的注意事项 如果系统中的一个策略家族的具体策略数量超过4个,则需要考虑使用混合模式,解决策略类膨胀和对外暴露的问题,否则日后的系统维护就会变成一个烫手山芋。 \ No newline at end of file diff --git a/src/dp/com/company/template_method/README.md b/src/dp/com/company/template_method/README.md index e44e0bb..93f10c9 100644 --- a/src/dp/com/company/template_method/README.md +++ b/src/dp/com/company/template_method/README.md @@ -1,30 +1,30 @@ -#模板方法模式(Template Method Pattern) -定义:Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure。定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 - -模板方法模式的通用类图如下图: -![Alt text](template.jpg "模板方法模式类图") - -模板方法模式确实非常简单,仅仅使用了Java的继承机制,但是它是一个应用非常广泛的模式。其中,AbstractClass叫做抽象模板,它的方法分为两类: - -- 基本方法。基本方法也叫做基本操作,是由子类实现的方法,并且在模板方法被调用。 -- 模板方法。可以有一个或几个,一般是一个具体方法,也就是一个骨架,实现对基本方法的调度,完成固定的逻辑。 - -在类图中还有一个角色:具体模板,ConcreteClass1和ConcreteClass2属于具体模板,实现父类所定义的一个或多个抽象方法,也就是父类定义的基本方法在子类中得以实现。 - - 注意:为了防止恶意的操作,一般模板方法都加上final关键字,不允许被覆写。 - -#模板方法模式的应用 -##模板方法模式的优点 - * 封装不变部分,扩展可变部分。把认为是不变部分的算法封装到父类实现,而可变部分的则可以通过继承来继续扩展。我们悍马模型例子中,是不是就非常容易扩展,例如增加一个H3型号的悍马模型,很容易呀,增加一个子类,实现父类的基本方法就可以了。 - * 提取公共部分代码,便于维护。我们例子中刚刚走过的弯路就是最好的证明,如果我们不抽取到父类中,任由这种散乱的代码发生,想想后果是什么样子?维护人员为了修正一个缺陷,需要到处查找类似的代码! - * 行为控制交由子类来实现。基本方法是由子类实现的,因此子类可以通过扩展的方式增加相应的功能,符合开闭原则。 - - -##模板方法模式的缺点 -按照我们设计习惯,抽象类负责声明最抽象、最一般的事物属性和方法,实现类完成具体的事物属性和方法,但是模板方法模式却颠倒了,抽象类定义了部分抽象方法,由子类实现,子类执行的结果影响了父类的结果,也就是子类对父类产生了影响,这在复杂的项目中,会带来代码阅读的难度,而且也会让新手产生不适感。 - - -#模板方法模式的使用场景 - * 多个子类有公有的方法,并且逻辑基本相同时。 - * 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。 +# 模板方法模式(Template Method Pattern) +定义:Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure。定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 + +模板方法模式的通用类图如下图: +![Alt text](template.jpg "模板方法模式类图") + +模板方法模式确实非常简单,仅仅使用了Java的继承机制,但是它是一个应用非常广泛的模式。其中,AbstractClass叫做抽象模板,它的方法分为两类: + +- 基本方法。基本方法也叫做基本操作,是由子类实现的方法,并且在模板方法被调用。 +- 模板方法。可以有一个或几个,一般是一个具体方法,也就是一个骨架,实现对基本方法的调度,完成固定的逻辑。 + +在类图中还有一个角色:具体模板,ConcreteClass1和ConcreteClass2属于具体模板,实现父类所定义的一个或多个抽象方法,也就是父类定义的基本方法在子类中得以实现。 + +> 注意:为了防止恶意的操作,一般模板方法都加上final关键字,不允许被覆写。 + +# 模板方法模式的应用 +### 1.模板方法模式的优点 + * 封装不变部分,扩展可变部分。把认为是不变部分的算法封装到父类实现,而可变部分的则可以通过继承来继续扩展。我们悍马模型例子中,是不是就非常容易扩展,例如增加一个H3型号的悍马模型,很容易呀,增加一个子类,实现父类的基本方法就可以了。 + * 提取公共部分代码,便于维护。我们例子中刚刚走过的弯路就是最好的证明,如果我们不抽取到父类中,任由这种散乱的代码发生,想想后果是什么样子?维护人员为了修正一个缺陷,需要到处查找类似的代码! + * 行为控制交由子类来实现。基本方法是由子类实现的,因此子类可以通过扩展的方式增加相应的功能,符合开闭原则。 + + +### 2.模板方法模式的缺点 +按照我们设计习惯,抽象类负责声明最抽象、最一般的事物属性和方法,实现类完成具体的事物属性和方法,但是模板方法模式却颠倒了,抽象类定义了部分抽象方法,由子类实现,子类执行的结果影响了父类的结果,也就是子类对父类产生了影响,这在复杂的项目中,会带来代码阅读的难度,而且也会让新手产生不适感。 + + +# 模板方法模式的使用场景 + * 多个子类有公有的方法,并且逻辑基本相同时。 + * 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。 * 重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数(见“模板方法模式的扩展”)约束其行为。 \ No newline at end of file diff --git a/src/dp/com/company/visitor/README.md b/src/dp/com/company/visitor/README.md index be6c389..29e893a 100644 --- a/src/dp/com/company/visitor/README.md +++ b/src/dp/com/company/visitor/README.md @@ -1,39 +1,39 @@ -#访问者模式(Visitor Pattern) -定义:Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operations.(封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。) - - -访问者模式的通用类图如下图: -![Alt text](visitor.jpg "访问者模式类图") - -我们来看看这几个角色的职责。 - -- IVisitor抽象访问者:抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法的参数定义哪些对象是可以被访问的。 -- Visitor具体访问者:它影响访问者访问到一个类后该怎么干,要做什么事情。 -- Element抽象元素:接口或者抽象类,声明接受哪一类访问者访问,程序上通过accept方法中的参数来定义的。 -- ConcreteElement具体元素:实现accept方法,通常是visitor.visit(this),基本上都形成了一种模式了。 -- ObjectStruture结构对象:元素产生者,一般容纳在多个不同类、不同接口的容器,如List、Set、Map等,在项目中,一般很少抽象出这个角色。 - - -#访问者模式的应用 -##1.访问者模式的优点 - * 符合单一职责原则:具体元素角色也就是抽象类的两个子类负责数据的加载,而Visitor类则负责报表的展现,两个不同的职责非常明确地分离开来,各自演绎变化。 - * 优秀的扩展性:由于职责分开,继续增加对数据的操作是非常快捷的。 - * 灵活性非常高。 - - -##2.访问者模式的缺点 - * 具体元素对访问者公布细节:访问者要访问一个类就必然要求这个类公布一些方法和数据,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的。 - * 具体元素变更比较困难。 - * 违背了依赖倒置原则:访问者依赖的是具体元素,而不是抽象元素,这破坏了依赖倒置原则,特别是在面向对象的编程中,抛弃了对接口的依赖,而直接依赖实现类,扩展比较难。 - - -##3.访问者模式的使用场景 - * 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作,也就是用迭代器模式已经不能胜任的情景。 - * 需要对一个对象结构中的对象进行很多不同并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。 - - -总结一下,在这种地方你一定要考虑使用访问者模式:业务规则要求遍历多个不同的对象。这本书也是访问者模式出发点,迭代器模式只能访问同类或同接口的数据,而访问者模式是对迭代器模式的扩充,可以遍历不同的对象,然后执行不同的操作,也就是针对访问的对象不同,执行不同操作。访问者模式还有一个用途,就是冲动拦截器角色。 - - -##4.访问者模式的最佳实践 +# 访问者模式(Visitor Pattern) +定义:Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operations.(封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。) + + +访问者模式的通用类图如下图: +![Alt text](visitor.jpg "访问者模式类图") + +我们来看看这几个角色的职责。 + +- IVisitor抽象访问者:抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法的参数定义哪些对象是可以被访问的。 +- Visitor具体访问者:它影响访问者访问到一个类后该怎么干,要做什么事情。 +- Element抽象元素:接口或者抽象类,声明接受哪一类访问者访问,程序上通过accept方法中的参数来定义的。 +- ConcreteElement具体元素:实现accept方法,通常是visitor.visit(this),基本上都形成了一种模式了。 +- ObjectStruture结构对象:元素产生者,一般容纳在多个不同类、不同接口的容器,如List、Set、Map等,在项目中,一般很少抽象出这个角色。 + + +# 访问者模式的应用 +### 1.访问者模式的优点 + * 符合单一职责原则:具体元素角色也就是抽象类的两个子类负责数据的加载,而Visitor类则负责报表的展现,两个不同的职责非常明确地分离开来,各自演绎变化。 + * 优秀的扩展性:由于职责分开,继续增加对数据的操作是非常快捷的。 + * 灵活性非常高。 + + +### 2.访问者模式的缺点 + * 具体元素对访问者公布细节:访问者要访问一个类就必然要求这个类公布一些方法和数据,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的。 + * 具体元素变更比较困难。 + * 违背了依赖倒置原则:访问者依赖的是具体元素,而不是抽象元素,这破坏了依赖倒置原则,特别是在面向对象的编程中,抛弃了对接口的依赖,而直接依赖实现类,扩展比较难。 + + +### 3.访问者模式的使用场景 + * 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作,也就是用迭代器模式已经不能胜任的情景。 + * 需要对一个对象结构中的对象进行很多不同并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。 + + +总结一下,在这种地方你一定要考虑使用访问者模式:业务规则要求遍历多个不同的对象。这本书也是访问者模式出发点,迭代器模式只能访问同类或同接口的数据,而访问者模式是对迭代器模式的扩充,可以遍历不同的对象,然后执行不同的操作,也就是针对访问的对象不同,执行不同操作。访问者模式还有一个用途,就是冲动拦截器角色。 + + +### 4.访问者模式的最佳实践 访问者模式是一种集中规整模式,特别适用于大规模重构的项目,在这一个阶段需求已经非常清晰,原系统的功能点也已经明确,通过访问者模式可以很容易把一些功能进行梳理,达到最终目的——功能集中化,如一个统一的报表运算、UI展现等,我们还可以与其它模式混编建立一套自己的过滤器或者拦截器。 \ No newline at end of file