随着设计模式的广泛使用,如何在结构图(主要是UML类图)中标注设计模式成为大家讨论的一个热点话题。设计模式是软件设计中的一些微结构,通过一种合理的方法来标注设计模式既有助于开发人员更好地进行设计软件系统,也有利于理解一些遗留系统,具体来说,设计模式的标注具有以下意义:
(1) 在系统设计和实现阶段,如果能够通过一种简单易懂的方式来标注相应的模式角色,将有助于开发人员开发和设计软件时记录所采用的解决方案,便于及时修改和完善设计方案,也有助于更好地理解和修改源代码;
(2) 在系统维护阶段,由于软件文档的缺失或核心开发人员的离职等原因将导致遗留系统的代码阅读和维护任务非常艰巨,一个复杂的系统往往包含成百上千个文件,这些文件之间的关系错综复杂,很难在较短的时间内理解它们之间的关系,明确设计人员的设计思想和意图,如果能够在设计文档和源代码中对软件中所使用设计模式进行标注,无疑会大大简化对系统的理解难度。
UML只能以可视化的方式描述系统的组成元素以及它们之间的关系,并不能揭示其中蕴含的设计模式相关信息。如图1所示的某图表显示系统设计结构图,在该结构图中使用了两个设计模式,分别是桥接模式(Bridge
Pattern)和适配器模式(Adapter Pattern),其中抽象图表类Chart充当桥接模式中的抽象类(Abstraction)角色,饼状图类PieChart和柱状图类BarChart充当扩充抽象类(RefinedAbstraction)角色,数据读取接口DataReader充当实现类接口(Implementor)角色,数据库读取类DBReader和Excel读取类ExcelReader充当具体实现类(ConcreteImplementor)角色;同时,由于ExcelReader重用了现有类ExcelAPI,因此在设计方案中又使用了适配器模式,DataReader充当适配器模式中的目标抽象类(Target)角色,ExcelReader充当适配器(Adapter)角色,ExcelAPI充当被适配的适配者(Adaptee)角色。如果没有相应的文字说明和丰富的设计模式使用经验,从图1中要准确而又快速地识别出其中使用的模式和每一个模式角色的难度较大。因此,对于系统的设计人员、实现人员和维护人员而言,如何在软件设计方案中标注设计模式意义重大
近年来,陆续诞生了一些设计模式标注方法,各具特色,也都存在一些问题和不足,下面我对这些标注方法做一个简单的介绍:
1. 维恩图风格模式标注
维恩图风格模式标注(Venn Diagram-Style Pattern
Annotation)是最早的设计模式标注方法之一,它由设计模式先驱John Vlissides提出,其表示方式如图2所示。在图2中,通过实线框和不同的阴影颜色来区分不同的设计模式,这种标注方式简单易懂,但存在很多问题,例如当一个类在多个设计模式中都扮演相应的角色时,将导致实线框的多次重叠,影响结构图的可读性;此外使用不同的阴影颜色来代表不同的模式也将导致颜色的多层重叠和文档打印的不便;这种方式还有一个缺点是只能标注使用了哪些设计模式,而不能对其中的模式元素进行标注,不能标注类模式角色、方法、属性等重要信息。
个人观点:该方法简单,但是模式太多就会很乱!
2. 虚线边界模式标注
虚线边界模式标注(Dotted Bounding Pattern Annotation)方法由德克萨斯大学达拉斯分校的Jing
Dong等提出,如图3所示。这种方法通过虚线框来解决维恩图风格模式标注方法中存在的阴影重叠问题,但是对于标注信息太过简单、模式重叠区域线段较多等问题仍然无法解决。
个人观点:该方法也比较简单,但模式太多还是会很乱!
3. UML协作标注
UML协作标注(UML Collaboration Notation)方法最早由UML创始人Grady
Booch等提出,最开始是用于对通用UML图形中的一些元素进行描述,John Vlissides将其引入设计模式标注,在这种标注方式中,将所使用的设计模式名称放在虚线椭圆符中,再通过虚线指向具体的模型元素,在虚线上注明该模型元素对应的模式角色名,如图4所示。但是在这种标注方式中,大量虚线的使用将让整个结构图变得非常凌乱,同时由于模式信息和类结构信息混合在一起,导致这两类信息都难以理解和识别,此外,这种方法虽然可以标注每一个类所扮演的模式角色信息,但是不能标注属性、方法等模式信息。
个人观点:模式不多时还不错,清晰明了,但模式较多时会很乱,线太多了!
4. “模式:角色”标注
“模式:角色”标注(Pattern : Role Annotations)方法由设计模式先驱Eric
Gamma等提出,该方法通过阴影框来标注模式名称和模式角色名称,为了节省篇幅,如果某个元素只在一个模式中充当某个角色,则可省略模式名,直接用“模式角色名”的方式来标注角色,如图5所示。这种模式标注方式直观易懂,但是在复杂结构中,“模式:角色”标注方法将会导致结构图变得非常复杂,每一个与模式相关的类都需要对应增加一个阴影框,导致结构图中充斥太多图形元素,N个类可能需要2N个阴影框,而且阴影框的位置也很重要,不合适的位置将导致结构图非常拥挤;此外,该方法也没有考虑到方法和属性等细节信息的标注;同时,如果某一个类在同一模式的多个实例中都扮演了某个角色,该方法不能明确标识出不同模式实例中的不同角色。
个人观点:元素太多,布局太麻烦,如果类图太复杂该方法不合适,在Joshua
Kerievsky的《重构与模式》一书中,很多图就是用这种方式来标注的(注:图都不是很复杂)!
5. 基于标记的模式标注
美国德克萨斯大学达拉斯分校的Jing Dong等人提出了一种基于标记的模式标注方法(Tagged
Pattern Notation)。该方法通过向原有结构图中附加标记值(Tagged Value)的方式来对模式信息进行标注,每一个类可使用“{pattern[instance]:role}”的方式来描述,例如“{Adapter[1]:Target}”表示该类在第一个适配器模式实例中充当抽象目标类角色。如果不存在模棱两可的情况,可以对描述方式进行简化,例如某个类只出现在某个模式的唯一实例中,则可简化为“{pattern:role}”,如果某个类只与一个模式的一个实例相关,则可进一步简化为“{role}”。在这种标注方法中,可以加入多个标记值来对同时充当多种模式的某一角色的类进行标注;在标注时没有增加一些额外的符号和线段,因此没有给结构图的复杂度带来太多影响;此外,该方法还可以标注属性、方法等细节信息,这是目前最好的模式标注方法之一,如图6所示。但是该方法也存在一些问题,很多已有的CASE工具不能够方便地增加标记值,需要通过编程来扩展它们的功能,加大使用的难度;此外,它没有提供一种在源代码中标注模式信息的方式,如果开发人员希望在实现过程中记录模式信息,该方法尚未提供相应的标注方案。
(偶不知道Jing Dong同志用啥工具画的此图,我用Visio画的,画得好辛苦,,找了好几个工具,发现都没有办法增加Tagged
Value,郁闷啊!)
个人观点:该方法不太复杂,但是貌似很多CASE工具增加标记值都不是特别方便!
6. 基于UML Profile的模式标注
基于UML Profile的模式标注方法(A UML Profile for
Design Patterns Notation)也是由Jing Dong(真是牛人啊,弄出这么多方法,据此还发了几篇不错的文章,有TSE哦,)等人提出来的,该方法结合了UML的两种扩展机制:衍型(Stereotype)和标记值(Tagged
Value),在该方法中,使用衍型<<PatternClass>>来标注某个类是设计模式的一部分,使用衍型<<PatternAttribute>>来标注某个属性是设计模式的一部分,使用衍型<<PatternOperation>>来标注某个方法是设计模式的一部分;然后再使用标记值对上述三种衍型进行详细的描述,对于类而言,标记值的格式为{role@pattern[instance]}[1..*],role表示模式角色名,pattern表示设计模式名,instance表示实例编号,[1..*]表示这种标注格式对于一个类来说可以重复1次或多次(一个类可以在多个模式实例中充当相应的角色),对于方法和属性而言,标注方法与类的标注方法相似。使用基于UMLProfile的模式标注方法的标注实例如图7所示。
(偶一直未能找到一个UML Profile的最恰当的翻译)
个人观点:该方法还是略为有点复杂,包含了一些重复信息,图会变得有点大!
7. 基于衍型的设计模式标注方法
Stefan Berner等最早将衍型用于标注模式信息,他们将衍型分为四类,分别是装饰衍型(Decorative
Stereotype)、描述衍型(Descriptive Stereotype)、限制衍型(Restrictive
Stereotype)和重定义衍型(Redefining Stereotype)。通过衍型可以给UML模型元素增加一些属性或者描述信息,但是Berner的衍型标注并不完备,没有提供一套完整的衍型标注方式,缺乏对属性和方法等的描述,也未提供源代码中模式信息的标注方案。之后,Jing
Dong等人提出了一些通过扩展UML来对设计模式进行可视化的方法,其中就包括使用衍型来描述类图中的模式信息,但是其标注形式较为复杂,需要同时通过衍型和标记值来表示与模式相关的信息,由于标注语句太长,该方法的使用受到一定的局限;与其他方法类似,Jing
Dong等人提出的模式信息标注方法也未提供源代码标注方案。
基于Berner和Jing Dong等人的工作,我们提出了一套完整、简单、且容易通过现有CASE工具实现的设计模式标注方法——基于衍型的模式标注(Stereotype
Based Pattern Notation, SBPN),该方法结合了现有模式标注方法的优点,既可以对结构图中的模式信息进行标注,还可以用于对源代码中的模式信息进行标注。SBPN没有给原有图形增加任何额外的图形元素,标注方式简单易学,可以容易在学术界和工业界推广。SBPN既可以用于标注类图,还可以用于标注交互图和源代码。
在SBPN方法中,可以通过在结构图(类图)中直接增加衍型来标注模式信息,标注规则如下:
【规则一】如果某个类只与某个设计模式的一个实例相关,则通过<<PatternName:RoleName>>的形式标注,其中PatternName表示模式名,RoleName表示对应的模式角色名,如<<Composite:Leaf>>。
【规则二】如果某个类涉及某个设计模式的多个实例,为了不引起歧义,更好地理解该类在各个实例中扮演的角色,通过<<PatternName[InstanceNo.]:RoleName>>的形式标注,其中PatternName和RoleName含义与【规则一】相同,InstanceNo.表示实例编号,对于同一种模式,从1开始进行编号,如<<Composite[1]:Composite>>。
【规则三】如果某个类涉及多个设计模式的一个或多个实例,则可以多次重复PatternName[InstanceNo.]:RoleName,两两之间通过分号(“;”)隔开,如<<Composite:Composite;Command:ConcreteCommand>>或<<Composite[1]:Composite;Command[1]:ConcreteCommand>>。
【规则四】如果需要标注某个方法/属性,如果所在的类使用【规则一】进行标注,则直接对方法/属性所扮演的角色进行标注即可,即标注该方法/属性对应于相应模式角色类中的哪个方法/属性,如<<execute>>;如果所在类使用【规则二】进行标注,为了不引起歧义,在进行方法/属性标注时也需要加上模式名称和编号,如<<Command[1]:execute>>;如果所在类使用【规则三】进行标注,为了不引起歧义,在进行方法/属性标注时也需要同时出现多个模式相关信息,如<<Command:execute;Composite:operation>>或<<
Command[1]:execute;Composite[1]:operation >>。
对于前面所述的图表显示系统,采用SBPN方法对其结构图进行标注后如图8所示:
除了对UML图形的标注外,SBPN方法还支持对源代码的标注,通过正向工程可以将结构图中的模式标注信息记录在源代码中,也可以通过逆向工程从源代码中获取模式相关信息,一方面便于对代码的阅读和理解,另一方面还有利于系统的扩展和维护。如图9所示某策略模式实例的结构图:
图9对应的Java代码片段如下所示,在源代码中,通过增加“@Stereoptype
PatternName:RoleName”形式的注释来对类进行标注,源代码的标注方式和结构图的标注方式类似,区别在于在源代码中使用“@Stereoptype”来取代UML图形中的双尖括号(“<<”和“>>”),同时还可以对源代码中的方法和属性进行标注。
/**
*@Stereotype Strategy:Context
*/
public class ArrayHandler {
/**
*@Stereotype strategy
*/
private Sort sortObj;
/**
*@Stereotype ContextInterface
*/
public int[] sort(int arr[]) {
sortObj.sort(arr);
return arr;
}
……
}
/**
*@Stereotype Strategy:Strategy
*/
public interface Sort {
/**
*@Stereotype AlgorithmInterface
*/
public int[] sort(int arr[]);
}
/**
*@Stereotype Strategy:ConcreteStrategy
*/
public class BubbleSort implements Sort {
/**
*@Stereotype AlgorithmInterface
*/
public int[] sort(int arr[]) {
……
}
} |
(SBPN方法已发表在2012年第11期的《计算机应用》中)
参考文献
[1] J.VLISSIDES. Notation, notation,
notation[J]. C++ Report, April 1998.
[2] GERARDO CEPEDA PORRAS, YANN-GA?L
GUéHéNEUC. An empirical study on the efficiency of different
design pattern representations in UML class diagrams[J].
Empirical Software Engineering, 2010, 15(5): 493-522.
[3] TRESE T, TILLEY S. Documenting software
systems with views V: towards visual documentation of
design patterns as an aid to program understanding[C].
In: Proceedings of the 25th international conference
on design of communication. ACM, New York, 2007, 103-112.
[4] JING DONG, SHENG YANG AND KANG ZHANG.
Visualizing design patterns in their applications and
compositions[J]. IEEE Transactions on Software Engineering,
2007, 33(7): 433-453.
[5] JING DONG. Adding pattern related
information in structural and behavioral diagrams[J].
Information and Software Technology, 2004, 46(5): 293-300.
[6] JING DONG. UML extensions for design
pattern compositions[J]. Journal of Object Technology,
2002, 1(5): 149-161.
[7] MARTIN GOGOLLA, BRIAN HENDERSON-SELLERS.
Analysis of UML stereotypes within the UML metamodel[C].
In: Proceedings of the 5th International Conference
on the Unified Modeling Language(UML' 2002) , LNCS(Lecture
Notes in Computer Science), 2002, 2460: 84-99.
[8] STEFAN BERNER, MARTIN GLINZ, STEFAN
JOOS. A classification of stereotypes for object-oriented
modeling languages[C]. In: Proceedings of the 2nd International
Conference on the Unified Modeling Language(UML' 1999),
LNCS(Lecture Notes in Computer Science), 1999, 1723:
249-264.
[9] 蒋严冰,邵维忠,张路,麻志毅. UML中衍型的精确定义与分析[J].
电子学报, 2003, 31(12A): 2101-2105.
[10] 刘伟, 胡志刚. 基于衍型的模式标注方法[J]. 计算机应用,
2012, 32(11): 3062-3066. |