1
\$\begingroup\$

I have a question about the best way to design / redesign my software. I have a program that uses a web services to perform certain tasks. Each task is a different set of request / response classes on the server end and a general function call from the Main Hook.

RequestObject req = new RequestObject("SomeVal");
ResponseObject rsp = new ResponseObject();

rsp = WebServiceHook.Method1(RequestObject); //Returns ResponseObject

Each Method takes a different request object and returns a different type of response object.

As it stands, I have a class for each of these methods. Each class has a public method Process() that does the interaction. I am trying to determine the best way to group all this code together using OO techniques without sacrificing functionality. I would like just one ProcessMethod in one class that will handle the interaction with the web service for all the different web methods.

From reading about the OO patterns, it would appear the one I need to use is the abstract factory pattern with the adapter and strategy pattern thrown in too.

Am I going about this the right way?

I have a COM Library created in C#, with COMVisible set to true. This library talks to a 3rd party WebService calling various methods depending on the task at hand. For each Request / Response class exposed by the 3rd party DLL I have a Request / Response Pair (Class and Interface) to Marshal variables specifically for a VC6 application.

Mainly using:

MarshalAs(UnmanagedType.LPStr)]

I have added a C# project to the solution to test this code, see test method below:

static void Main(string[] args)
        {
            ICOMReq iReq = new CCOMReq();
            ICOMRsp iRsp = new CCOMRsp();
            Link l = new Link();

            iReq.Date_Time = DateTime.UtcNow;
            iReq.Var2 = "1023758145865";

            l.DoMethod1(iReq, out iRsp);

            //Handle COM Response here
        }

The link class looks like so at the moment, but will have all the methods created once the design is correct:

public class Link
    {
        public bool DoMethod1(ICOMReq _COMReq, out ICOMRsp COMRsp)
        {
            Method1 WebServiceMethod = new Method1(new Method1Request(), new Method1Response(), (CCOMReq)_COMReq, new CCOMRsp());
            WebServiceMethod.Process();

            //Just test code at the moment
            COMRsp = null;
            return true;
        }
    }

Each DoMethod(N) that will be in the link class will look the same performing its task with identical code to the other DoMethods. The key differences between the methods is the Param Types Passed in and the Method1 (Method1Request/Method1Response) type will vary depending on the webmethod to be called.

Class Method1 (There will be one of these for every method I need to implement on the WebService) looks like so:

public class Method1 : WebServiceInterfaceBridge<Method1Request, Method1Response, ICOMReq, ICOMRsp>
{
    public Method1(Method1Request WEB_Req, Method1Response WEB_Rsp, CCOMReq COM_Req, ICOMRsp COM_Rsp)
        : base(WEB_Req, WEB_Rsp, CBE_Req, CBE_Rsp) 
    {
        WEB_Req.SOME_DATE_TIME = COM_Req.Date_Time;
    }

    public new void Process()
    {
        base.Process();
    }

    public override void LOG()
    {
        Console.WriteLine(this.WebMethod_Request.MEMBER_VAR_HERE);
    }

    //There will be additional methods here in the end as well as a mapping method to convert the WEBMethodResponse to a COMResponse and pass it back to the caller
}

The class (WebServiceInterfaceBridge) that all Method(N) classes will inherit from is shown below:

public abstract class WebServiceInterfaceBridge <T,U,V,W> : WebServiceInterface
    where T : class 
    where U : class
    where V : class
    where W : class
{
    protected T WebMethod_Request;
    protected U WebMethod_Response;
    protected V COM_Request;
    protected W COM_Response;

    public WebServiceInterfaceBridge(T tPHSReq, U uPHSRsp, V vCBEReq, W wCBERsp)
    {
        WebMethod_Request = tPHSReq;
        WebMethod_Response = uPHSRsp;
        COM_Request = vCBEReq;
        COM_Response = wCBERsp;
    }

    public abstract void LOG();

    public void CallWebServiceMethod()
    {
        WebMethod_Response = CallWebMethod<T, U>(WebMethod_Request);
    }

    public void Process()
    {
        LOG();
        CallWebServiceMethod();
    }
}

And finally, here is the class that actually calls the Web service that the WebServiceBridge Class inherits from:

public class WebServiceInterface
    {
        private ServiceClient WEBSVC = new ServiceClient();

        public U CallWebMethod<T, U>(T tRequest) 
            where T:class 
            where U:class
        {
            if (tRequest.GetType() == typeof(Method2Request))
            {
                Method2Request Req = (Method2Request)(object)tRequest;
                Method2Response Rsp = WEBSVC.Method2(Req);
                return (U)(object)Rsp;
            }
            else if (tRequest.GetType() == typeof(Method1Request))
            {
                Method1Request Req = (Method1Request)(object)tRequest;
                Method1Response Rsp = WEBSVC.Method1(Req);
                return (U)(object)Rsp;
            }
            else
            {
                throw new NullReferenceException();
            }
        }
    }

I have left out my internal classes for marshaling the Req/Rsp's, they are just classes with Member Vars and Get/Sets.

Before I carry on with my design, I am wondering, from an OO point of view, if I am approaching this task in the right way or if what I am doing is overkill and could be greatly simplified?

\$\endgroup\$
6
  • 2
    \$\begingroup\$ Is this the real example? If it is, then i fail to see what is the purpose of Method1, Method2, etc. and why cant you have a single WebServiceInterface, which will handle all the requests/responses. \$\endgroup\$
    – Nikita B
    Jul 31, 2013 at 11:46
  • 2
    \$\begingroup\$ Also classes like WebServiceInterfaceBridge <T,U,V,W> is almost always an indication, that you are not going the right way :) \$\endgroup\$
    – Nikita B
    Jul 31, 2013 at 11:49
  • \$\begingroup\$ Hi Nik, Yes this is a real example with sensitive code obscured, Method1 & Method2 of the webservice actually performs tasks like updating databases or creating tickets etc. \$\endgroup\$ Jul 31, 2013 at 11:52
  • \$\begingroup\$ "Also classes like WebServiceInterfaceBridge <T,U,V,W> is almost always an indication, that you are not going the right way :) " - Ok how do i achieve what i need without generic types? \$\endgroup\$ Jul 31, 2013 at 11:53
  • \$\begingroup\$ By using interfaces/baseclasses. I mean, there is nothing wrong with generics, its just that in your case using 4 generic parameters looks like an overkill. \$\endgroup\$
    – Nikita B
    Jul 31, 2013 at 12:00

2 Answers 2

4
\$\begingroup\$

You're definitely going in a wrong direction. Instead of trying to synthesize some weird generic classes in attempt to find a common general pattern in "I send request and receive a reply, in each case requests and replies are different classes", you should start from designing the interface of your communication, so that other layers of your system are abstracted from technical implementation of COM/Web service/whatever protocol you have to deal with.

Based on your comment:

Method1 & Method2 of the webservice actually performs tasks like updating databases or creating tickets etc

we can define an interface like this:

public interface ITicketManager //bad name, just as an example
{
    Ticket CreateTicket(string ticketName, string project /*, etc*/);
    bool TryMoveTicket(Ticket ticket, string newProject);
    Ticket CreateSubTicket(Ticket parent, string ticketName);
}

As you see users of this interface don't know about those request-reply wrappers that you have in the underlying communication protocol, they are dealing with business entities. And implementation of this interface may look like this (just as example):

public class TicketManager: ITicketManager
{
    public Ticket CreateTicket(string ticketName, string project)
    {
        CreateTicketRequest request = new CreateTicketRequest
        {
            Name = ticketName,
            Project = project
        };
        var response = WebServiceHook.Method1(request);
        return new Ticket
        {
            Id = response.Id,
            Name = response.TicketName,
            Project = response.Project
        }
    }

    public bool TryMoveTicket(Ticket ticket, string newProject)
    {
        MoveTicketRequest request = new MoveTicketRequest
        {
            Id = ticket.Id,
            FromProject = ticket.Project,
            NewProject = newProject
        };
        var response = WebServiceHook.Method2(request);

        if (response.Successful)
            ticket.Project = newProject;

        return response.Successful;
    }

    public Ticket CreateSubTicket(Ticket parent, string ticketName)
    {
        CreateTicketRequest request = new CreateTicketRequest
        {
            ParentId = parent.Id,
            Name = ticketName,
            Project = project
        };
        var response = WebServiceHook.Method1(request);

        return new Ticket
        {
            Id = response.Id,
            Parent = parent,
            Name = response.TicketName,
            Project = response.Project
        }
    }
}
\$\endgroup\$
0
\$\begingroup\$

I think what you should do, is instead of spawning those Method classes you should implement a proper wrappers for com requests/responses, and then find the way to treat those wrappers the same way without the need to know, whats inside them. From the code you provided, i cant quite see whats stopping you. Off the bat, why cant you do something like:

interface IComWrapper
{
    object GetComObject();
    //something else? 
}

abstract class ComWrapperBase<T> : IComWrapper
{
    public virtual object GetComObject()
    {
        return BaseObject;
    }

    protected ComWrapperBase(T baseObject)
    {
        BaseObject = baseObject;
    }

    protected T BaseObject { get; private set; }
}

class Request1 : ComWrapperBase<CCOMReq>
{
    public Request1(CCOMReq baseObject) : base(baseObject)
    {}

    public DateTime DateTime
    {
        get { return BaseObject.Date_Time;  }
        set { BaseObject.Date_Time = value; }
    }

    public override string ToString()
    {
        return "Something you might want to log?";
    }

    //mapping
}

class Response1 : ComWrapperBase<ICOMRsp>
{
    public Response1(ICOMRsp baseObject) : base(baseObject)
    {}

    //mapping
}

interface IComService
{
    object Send(object comRequest);
}

interface ILogging
{
    void Log(object sender, string message);
}

interface IWrappersFactory
{
    T CreateWrapper<T>(object comObject);
}

class WebServiceInterface
{
    private readonly IComService _service;
    private readonly ILogging _log;
    private readonly IWrappersFactory _factory;

    public WebServiceInterface(IComService service, ILogging log, IWrappersFactory factory)
    {
        _service = service;
        _log = log;
        _factory = factory;
    }

    public TResponse Send<TRequest, TResponse>(TRequest request) 
        where TRequest : IComWrapper 
        where TResponse : IComWrapper
    {
        _log.Log(GetType(), request.ToString());
        var res = _service.Send(request.GetComObject());
        return _factory.CreateWrapper<TResponse>(res);
    }
}

//an example of how ComService implementation might look like (there is probably a better way)
class ComService : IComService
{
    private readonly ServiceClient WEBSVC = new ServiceClient();

    //depending on ServiceClient implementation, this probably can be simplified
    public object Send(object comRequest)
    {
        var r1 = comRequest as CCOMReq;
        if (r1 != null)
        {
            return WEBSVC.Method1(r1);
        }
        //handle other messages
        //.....
        //.....
        throw new NotSupportedException();
    }
}

Basically, what you are trying to do in you application is to ipmlement the same f**d up design that your ClientService has. What you should try to do instead is hide this design behind a bridge, and work with it dotNet-way using common OOP practices (meaning you should try to implement single generic method which sends all requests).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.