\subsubsection{Converting a Class into a Singleton} | |
\label{sec:ClassToSingleton} | |
The singleton pattern \cite{Larman} is applied when there is a class for which only one instance can exist at a time. In terms of UML, a singleton is a class stereotyped with the $<<singleton>>$ stereotype, and it defines a static attribute named \emph{instance} which holds the value of the unique instance. It also defines a static \emph{getInstance()} operation that returns that unique instance. Wizard \emph{ClassToSingleton}, presented in Listing \ref{lst:ClassToSingleton}, simplifies the process of converting a class into a singleton by adding the proper stereotype, attribute and operation to it automatically. | |
\begin{lstlisting}[ | |
basicstyle=\ttfamily\footnotesize, | |
flexiblecolumns=true, | |
numbers=none, | |
nolol=true, | |
caption=Implementation of the ClassToSingleton Wizard, | |
label=lst:ClassToSingleton, | |
numbers=left, | |
language=EWL, | |
tabsize=2 | |
] | |
wizard ClassToSingleton { | |
// The wizard applies when a class is selected | |
guard : self.isTypeOf(Class) | |
title : "Convert " + self.name + " to a singleton" | |
do { | |
// Create the getInstance() operation | |
var gi : new Operation; | |
gi.owner = self; | |
gi.name = "getInstance"; | |
gi.visibility = VisibilityKind#vk_public; | |
gi.ownerScope = ScopeKind#sk_classifier; | |
// Create the return parameter of the operation | |
var ret : new Parameter; | |
ret.type = self; | |
ret.kind = ParameterDirectionKind#pdk_return; | |
gi.parameter = Sequence{ret}; | |
// Create the instance field | |
var ins : new Attribute; | |
ins.name = "instance"; | |
ins.type = self; | |
ins.visibility = VisibilityKind#vk_private; | |
ins.ownerScope = ScopeKind#sk_classifier; | |
ins.owner = self; | |
// Attach the <<singleton>> stereotype | |
self.attachStereotype("singleton"); | |
} | |
} | |
// Attaches a stereotype with the specified name | |
// to the Model Element on which it is invoked | |
operation ModelElement attachStereotype(name : String) { | |
var stereotype : Stereotype; | |
// Try to find an existing stereotype with this name | |
stereotype = Stereotype.allInstances.selectOne(s|s.name = name); | |
// If there is no existing stereotype | |
// with that name, create one | |
if (not stereotype.isDefined()){ | |
stereotype = Stereotype.createInstance(); | |
stereotype.name = name; | |
stereotype.namespace = self.namespace; | |
} | |
// Attach the stereotype to the model element | |
self.stereotype.add(stereotype); | |
} | |
\end{lstlisting} | |
The \emph{guard} part of the wizard specifies that it is only applicable when the selection is a single UML class. The \emph{title} part specifies a context-aware title that informs the user of the functionality of the wizard and the \emph{do} part implements the functionality by adding the \emph{getInstance} operation (lines 10-14), the \emph{instance} attribute (lines 23-28) and the $<<singleton>>$ stereotype (line 31). | |
The stereotype is added via a call to the \emph{attachStereotype()} operation. Attaching a stereotype is a very common action when refactoring UML models, particularly where UML profiles are involved, and therefore to avoid duplication, this reusable operation that checks for an existing stereotype, creates it if it does not already exists, and attaches it to the model element on which it is invoked has been specified. | |
An extended version of this wizard could also check for existing association ends that link to the class and for which the upper-bound of their multiplicity is greater than one and either disallow the wizard from executing on such classes (in the $guard$ part) or update the upper-bound of their multiplicities to one (in the $do$ part). However, the aim of this section is not to implement complete wizards that address all sub-cases but to provide a better understanding of the concrete syntax and the features of EWL. This principle also applies to the examples presented in the sequel. |