In fact, in the ServiceManager definition, things are not as
simple as imaged with only two methods, StartService and StartAllServices. With
further examination, you can conclude ServiceManager should also include other
methods, such as RegisterService, UnregisterService, etc. However, simply making
use of the refactoring way "Extract Method," we cannot make more
improvements with the ServiceManager class even exhausting all related tricks. At
this time, we need to take up the "abstract" sword to build up a new type
of structure as shown in Figure 2.
Figure 2
For the reader more clearly to hold tight upon the essence
of problem, I abridged the two methods RegisterService and UnregisterService in
the definition of class ServiceManager. As seen from Figure 2, class ServiceManager
at this time should be an abstract one and its every method should all be defined
as related to the instance object.
Now, let us delve into the main methods in class ServiceManager.
First, the responsibility of method Initialize is to read the configuration
document information, initialize the service object and add it into the m_serviceList
object. This method will be invoked by the constructor function and used when the
instances of sub-classes are created. On the other hand, there are maybe
differences in special initializations when the instances of sub-classes
initialize service objects (and the service objects to be initialized may also
be different). In this case, we can conclude that it is unnecessary for class
ServiceManager to initialize the service objects any more. Therefore, the
Initialize method is defined as a protective abstract method.
The task of method LookUpServiceObject is still as before. It
seeks in the m_serviceList the corresponding service object and then returns it.
Because of the abstract reason, the m_serviceList object has now been defined
the related instantiation field. At this time, it is not fit to define method LookUpServiceObject
as a static method again. If we thoroughly inquiry into that method, we will
discover the realization of that method actually undertakes the task of facilitating
developers to start services, with not much relevance to the need of the client
side. So, according to the principle of "Information Hiding," we do
not have the necessity to expose such a method that has nothing to do with the
user of the class. For this, we should set it as the type of protect for the
purpose of encapsulation. Accordingly, we need to modify the StartService
method, setting the parameter as the name of the service object, and then calling
the LookUpServiceObject method inside the method to find out the corresponding
service object and work with it.
Now, the two methods of starting service are both modified
as public. Method StartService only starts a service object, while the
StartAllServices method starts all the matched service objects.
Next, let us take a closer look at the StartAllService
method. In this method, the m_serviceList object is looped through to locate
each matched service object, and then invoke its StartService method. Note that
although the m_serviceList object is acquired through method lnitialize, we still
need to judge the activating mode of the service object when the StartAllService
method is executed to avoid exceptions when starting the service object. However,
there exists a problem that we have to judge the activating mode of the service
object according to the type of the sub class-- we cannot obtain that
information in the parent class. As for method StartService, it is tightly relevant
to the type of the sub class-- the logic of starting services differs with different
kinds of activating modes of the service objects.
To resolve such a problem, we can fall back upon the
Template Method pattern to define a special abstract method GetActiveMode to
acquire activating modes of the service objects. The reason that we define method
GetActiveMode as an abstract method is the implementation of that method is accomplished
inside the sub classes, through which we can acquire the proper activating mode
of the service objects.
Similarly, the StartService method in the abstract class should
also be defined as an abstract one. As for the StartAllServices method, except
that the acquirement of the proper activating mode of the service objects needs
to be a sub class implementation, the behavior of looping through the m_serviceList
object has nothing to do with sub classes. Therefore, it can be implemented in
the parent class. Moreover, the other two methods GetActiveMode and
StartService will be called inside method StartAllServices. The related code is
shown in Listing 7.
Listing 7
public abstract class ServiceManager
{
protected List<ServiceObject> m_serviceList = new List<ServiceObject>();
public ServiceManager()
{
Initialize();
}
public List<ServiceObject> ServiceList
{
get { return m_serviceList; }
}
protected ServiceObject LookUpServiceObject(string serviceName)
{
foreach (ServiceObject obj in m_serviceList)
{
if (obj.ServiceName.Equals(serviceName))
{
return obj;
}
}
return null;
}
public void StartAllServices()
{
foreach (ServiceObject obj in m_serviceList)
{
if (obj.ActiveMode.Equals(GetActiveMode()))
{
StartService(obj.ServiceName);
}
}
}
protected abstract void Initialize();
protected abstract ActiveMode GetActiveMode();
public abstract void StartService(string serviceName);
}
Notice the access level of method GetActiveMode and
StartService. The StartService method is to be invoked by exterior classes, so
we set it as public. However, method GetActiveMode can only be called by
ServiceManager and its sub classes, so we set it as a protected method.
Although the StamAllServices method provides a concrete implementation,
it uses two abstract methods inside, which stay to be implemented in sub
classes. This is just like when we define a set of templates in the parent
class, and then provide a concrete implementation in sub classes according to
the related template. This is exactly what the Template Method pattern means.
For example, the definition of class SingletonServiceManager
is as follows.
Listing 8
public class SingletonServiceManager : ServiceManager
{
public SingletonServiceManager() : base() { }
protected override void Initialize()
{
//initialize all the service objects that
//use Singleton mode, and add them into
// the m_serviceList object…
}
protected override ActiveMode GetActiveMode()
{
return ActiveMode.Singleton;
}
protected override void StartService(string serviceName)
{
ServiceObject obj= LookUpServiceObject(
serviceName);
if (obj!=null)
{
If(obj.CurrentState.Equals(ServiceState.
Stop))
{
obj.CurrentState = ServiceState.Start;
}
}
}
Now, you see, by making use of an abstract way, we only did
very small adjustment to the whole structure, but this small one step set a
good keynote.