Practicing the Chain of Responsibility Pattern
page 6 of 8
by Xianzhong Zhu
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 33502/ 66

Falling Back on the Chain of Responsibility Pattern to Better the Design

Let us again recall the intent of the Chain of Responsibility 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." As you see, the advantage of the Chain of Responsibility pattern lies in that it is able, according to the specified condition, to locate the suitable object intelligently and performs its responsibility. The present design lacks some method that can judge given conditions intelligently and then instantiate the proper ServiceManager object.

According to the above analysis, to introduce the Chain of Responsibility pattern, we need to set up a linked table, like structure for class ServiceManager to store the ServiceManager typed objects. For this, we have to again modify class ServiceManager; Figure 3 shows the modified class diagram and its related derivations.

Figure 3: The modified ServiceManager class and its derivatives

Careful readers may catch sight of the difference between Figure 2 and Figure 3. Here, it is added a field m_manager of ServiceManager type. It is just through this field that we can achieve the Chain of Responsibility. Now, when we start the service, we need, according to the given conditions, to judge whether to execute the StartService method of current class. If not, then continue to judge the value of m_manager, and if m_manager is not empty, then invoke its related method.

For the sake of facilitating code reusing, I have also introduced a new method, IntemalStartService, with which to replace the concrete task of the old StartService method to be exclusively responsible for starting service objects. Because the implementation of the IntemalStartService method is decided by concrete sub classes, we define it as a protective abstract method. As for method StartService, due to the responsibility chain, it needs to acquire the activating mode of service objects. And since the activating mode is decided by judging the sub class type, it is defined as a public abstract method.

The key is the StartAllServices method. Now, this method undertakes two kinds of different responsibilities. One is to start all service objects; the other is to activate the related service object according to the specified activating mode. As a result, we need to define two methods both named StartAllServices, but with different signatures when defining the abstract ServiceManager class. In addition, we need to make small adjustment to the design of the responsibility chain pattern. In implementing method StartAllServices, whether the current object carries out this method or not, we have to carry out the StartAllServices method of the next object in the responsibility chain. Only in this way can we then start all the service objects. Because the StartAllServices method is relevant to the activating mode of service objects we still define it as a public abstract method.

In addition, in the abstract ServiceManager we have also defined a protected typed field m_hasNextManager to judge whether there exists the next object in the responsibility chain. Actually, the purpose of this field is to judge whether the m_manager object is empty. If empty, then the m_manager field is empty. When we add the ServiceManager object through method AddSeiviceManage we set the m_manager object to be empty.

Let us now take a look at the new definition of the abstract class ServiceManager, as shown in Listing 11.

Listing 11

public abstract class ServiceManager
{
    protected bool m_hasNextManager;
    protected ServiceManager m_manager;
    protected List<ServiceObject> m_serviceList = new List<ServiceObject>();
    public ServiceManager()
    {
        Initialize();
        m_hasNextManager = false;
    }
    public List<ServiceObject> ServiceList
    {
        get { return m_serviceList; }
    }
 
    protected virtual void Initialize()
    {
        //omit the detailed implementation...
    }
    
    protected ServiceObject LookUpServiceObject(string serviceName)
    {
        //just as before, omitted…
 
    }
    public void AddServiceManager(ServiceManager manager)
    {
        m_manager = manager;
        m_hasNextManager = true;
    }
    protected abstract ActiveMode GetActiveMode();
    protected abstract void InternalStartService(ServiceObject obj);
    //…(omitted)
}
 

Accordingly, we should modify the sub classes. Here, take class SingletonSetviceManager as an example.

Listing 12

public class SingletonServiceManager : ServiceManager
{
    public SingletonServiceManager() : base() { }
    protected override ActiveMode GetActiveMode()
    {
        return ActiveMode.Singleton;
    }
    
    protected override void InternalStartService(ServiceObject obj)
    {
        if (obj.CurrentState == ServiceState.Stop)
        {
            RemotingConfiguration.RegisterWellKnownServiceType(obj.ObjectType, 
              obj.ServiceName, WellKnownObjectMode.Singleton);
            obj.CurrentState = ServiceState.Start;
        }
    }
 
    protected override void StartService(string serviceName)
    {
        ServiceObject obj= LookUpServiceObject(
                  serviceName);
        if (obj!=null)
        {
           if(obj.CurrentState.Equals(ActiveMode.
                                    Singleton))
           {
           }
           else
                   {
                          if(m_hasNextManager)
                   {
                  m_manager.StartService(serviceName);
                            }
                    }
        }
    }
}
    public override void StartAllServices()
    {
      for(ServiceObject obj in m_serviceList)
      {
                  if(obj.ActiveMode.Equals(ActiveMode.Singleton))
            {
                  InternalStartService(obj);
            }
      }
      if(m_hasNextManager)
      {
            m_manager.StartAllServices();
      }
    }
    public override void StartAllServices(ActiveMode activeMode)
    {
      if(activeMode.Equals(ActiveMode.Singleton))
      {
            for(ServiceObject obj in m_serviceList)
            {
                  if(obj.ActiveMode.Equals(activeMode))
            {
                  InternalStartService(obj);
            }
            }
      else
             if(m_hasNextManager)
            {
                  m_manager.StartAllServices(activeMode);
            }
    }
}

Because the implementations of methods StartService and StartAllServices are both put into sub classes, at this time, in the parent class the two protect abstract methods GetActiveMode and IntemalStartService lose existent meaning. Our design, for now, obviously has some problems. By carefully examining all the sub classes of the parent class ServiceManager, it is not difficult to find that whether in the StartService method or the startAllSerVices method it still rests upon the value of ActiveMode to select the mode to run. Therefore, we still can pick up the Template Method pattern to abstract the similar implementation logic into the parent class, which can reduce the repeated code that is before defined in sub classes. Hence, the final design of class ServiceManager should look like that shown below.

Listing 13

public abstract class ServiceManager
{
    protected bool m_hasNextManager;
    protected ServiceManager m_manager;
    protected List<ServiceObject> m_serviceList = new List<ServiceObject>();
    public ServiceManager()
    {
        Initialize();
        m_hasNextManager = false;
    }
    public List<ServiceObject> ServiceList
    {
        get { return m_serviceList; }
    }
 
    public static ServiceManager GetServiceManager()
    {
        SingletonServiceManager stManager = new SingletonServiceManager();
        SingleCallServiceManager scManager = new SingleCallServiceManager();
        ActivationServiceManager acManager = new ActivationServiceManager();
        acManager.AddServiceManager(stManager);
        stManager.AddServiceManager(scManager);
        return acManager;
    }
 
    protected virtual void Initialize()
    {
        //omit the concrete implementation...
    }
    
    protected ServiceObject LookUpServiceObject(string serviceName)
    {
        foreach (ServiceObject obj in m_serviceList)
        {
            if (obj.ServiceName.Equals(serviceName))
            {
                return obj;
            }
        }
        return null;
 
    }
    public void AddServiceManager(ServiceManager manager)
    {
        m_manager = manager;
        m_hasNextManager = true;
    }
    public void StartService(string serviceName)
    {
        ServiceObject obj = LookUpServiceObject(serviceName);
        if (obj != null)
        {
 
            InternalStartService(obj);
        }
        else
        {
            if (m_hasNextManager)
            {
                m_manager.StartService(serviceName);
            }
        }           
    }
    public void StartAllServices()
    {
        foreach (ServiceObject obj in m_serviceList)
        {
            if (obj.ActiveMode.Equals(GetActiveMode()))
            {
                InternalStartService(obj);
            }
        }
        if (m_hasNextManager)
        {
            m_manager.StartAllServices();
        }
    }
    public void StartAllServices(ActiveMode activeMode)
    {
        if (activeMode.Equals(GetActiveMode()))
        {
            foreach (ServiceObject obj in m_serviceList)
            {
                if (obj.ActiveMode.Equals(activeMode))
                {
                    InternalStartService(obj);
                }
            }
        }
        else
        {
            if (m_hasNextManager)
            {
                m_manager.StartAllServices(activeMode);
            }
        }
    }
 
    public void StopService(string serviceName)
    {
        ServiceObject obj = LookUpServiceObject(serviceName);
        if (obj != null)
        {
 
            InternalStopService(obj);
        }
        else
        {
            if (m_hasNextManager)
            {
                m_manager.StopService(serviceName);
            }
        }
    }
    public void StopAllServices()
    {
        foreach (ServiceObject obj in m_serviceList)
        {
            if (obj.ActiveMode.Equals(GetActiveMode()))
            {
                InternalStopService(obj);
            }
        }
        if (m_hasNextManager)
        {
            m_manager.StopAllServices();
        }
    }
    public void StopAllServices(ActiveMode activeMode)
    {
        if (activeMode.Equals(GetActiveMode()))
        {
            foreach (ServiceObject obj in m_serviceList)
            {
                if (obj.ActiveMode.Equals(activeMode))
                {
                    InternalStopService(obj);
                }
            }
        }
        else
        {
            if (m_hasNextManager)
            {
                m_manager.StopAllServices(activeMode);
            }
        }
    }
    protected abstract ActiveMode GetActiveMode();
    protected abstract void InternalStartService(ServiceObject obj);
    protected abstract void InternalStopService(ServiceObject obj);
}

So, the definitions of the sub classes of class ServiceManager become much simpler. The crucial part in the class SingletonServiceManager is listed below. For more details and the other two sub classes SingleCallServiceManager and ActivationServiceManager, please refer to the download source code at the end of this article.

Listing 14

public class SingletonServiceManager : ServiceManager
{
    public SingletonServiceManager() : base() { }
 
    protected override ActiveMode GetActiveMode()
    {
        return ActiveMode.Singleton;
    }
    
    protected override void InternalStartService(ServiceObject obj)
    {
        if (obj.CurrentState == ServiceState.Stop)
        {
            RemotingConfiguration.RegisterWellKnownServiceType(obj.ObjectType, 
              obj.ServiceName, WellKnownObjectMode.Singleton);
            obj.CurrentState = ServiceState.Start;
        }
    }
      //…(omitted)
}

Due to reasonably making use of the Template Method pattern (a large part of the implementation logics are shifted into the abstract parent class), the definitions and implementations seem much concise. And again, because we have introduced the Chain of Responsibility pattern, the suitable service object can be found out intelligently, which greatly simplifies the caller. You can refer to the following code to find out this simplicity.

Listing 15

public partial class MainForm : Form
{
    private ServiceManager acManager;
    public MainForm()
    {
        InitializeServiceManager ();
    }
    private void InitializeServiceManager ()
      {
       SingletonServiceManager stManager = new SingletonServiceManager();
       SingleCallServiceManager scManager = new SingleCallServiceManager();
       acManager = new ActivationServiceManager();
       stManager.AddServiceManager(scManager);
       acManager.AddServiceManager(stManager);
      }
    private void munStartAll_Click(object sender, EventArgs e)
    {
        acManager.StartAllServices();
        InitListView();
    }
 
    private void startByServiceNameToolStripMenuItem_Click(object sender, EventArgs e)
    {
        if (lvService.SelectedItems.Count > 0)
        {
            string serviceName = lvService.SelectedItems[0].SubItems[0].Text;
            acManager.StartService(serviceName);
            InitListView();
        }
    }
}

As you have seen, we have built up a responsibility chain with the help of the InitializeServiceManager method, as shown in Figure 4. When you invoke the StartService method of the acManager object, no matter which kind of activating mode of the currently-selected remote object is, we can find out the appropriate matched ServiceManager object to execute.

Figure 4 - The Responsibility Chain Diagram

Although we have, in the above code, used the InitializeServiceManager method to succeed in establishing a responsibility chain; however, as I have pointed out previously, we would better provide a specialized class or method to be responsible for such kink of job. For simplicity, we can directly define a static method named GetServiceManager in class ServiceManager to bear this task.

Listing 16

namespace DonOfDesign.PracticeCORPattern.ServiceManagerLib
{
    public abstract class ServiceManager
    {
        public static ServiceManager GetServiceManager()
        {
            SingletonServiceManager stManager = new SingletonServiceManager();
            SingleCallServiceManager scManager = new SingleCallServiceManager();
            ActivationServiceManager acManager = new ActivationServiceManager();
            acManager.AddServiceManager(stManager);
            stManager.AddServiceManager(scManager);
            return acManager;
        }
   //…(omitted)

A very small improvement can vastly simplifies the calling code. In this way, we can remove the InitializeServiceManager method from class MainFom, and modify the code of the constructor function.

Listing 17

public MainForm()
{
    acManager =ServiceManager.GetServiceManager();
}

Now, let us take a quick look at the running order of method StartService of the acManager object from the point view of the caller. Suppose that the currently-selected service object will be called in the SingleCall way, and then it is executed in the following order:

(1) Invoke the StartService method of the ActivationServiceManager object.

(2) Since the GetActiveMode method returns a non-Activation value, execute the else branch.

(3) Judge whether hasNextManager is true or not. Because the acManager object invoked the AddServiceManager method, hasNextManager should be true, and the value of the private field m_manager is stManager.

(4) Invoke the StartService method of the SingletonServiceManager object.

(5) Repeat step (2), and if the active mode still does not match, then execute the else branch.

(6) Because the stManager object invoked the AddServiceManager method, the value of hasNextManager was true and the value of m_manager is scManager. So, the StartService method of the SingleCallServiceManager object is invoked.

(7) At this time, the active mode matches the given condition; then the InternalStartService method of the SingleCallServiceManager object is called to start related service.

As for the StartAllServices method of the acManager object, it has hypostatic differentiation with the StartService method. Superficially seen, it only carried out the StartAllServices method of an object; however, it can access all the elements in the responsibility chain. As a result, it actually calls all the StartAllServices methods of acManager, stManager and scManager, which starts all service objects.


View Entire Article

User Comments

No comments posted yet.

Product Spotlight
Product Spotlight 





Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-04-25 7:36:54 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search