组合两个模块而不使工厂成为上帝对象

时间:2015-03-11 20:40:37

标签: c# design-patterns module decorator

我们有一个应用程序可以联系多个不同的远程服务(SOAP,HTTPREQUEST)。然后我们执行不同的操作(导入,导出,更新,删除)。

今天我们有两个客户类四个动作类

问<!/强>
如何解耦这两个模块,以便我必须做最少的更改。 IE只添加一个新动作/新客户端。没什么。

客户端类
授权我们的客户端远程服务,它处理登录和退出。

行动类
持有url,方法来调用客户端。以及ExecuteActionMethod

用法
客户端类通过操作进行修饰,然后与客户端一起执行操作。

恐惧
我不希望:
- 每次添加新的客户端类时创建一个新的操作类 - 创建一个新的客户端类< / strong>每次我添加一个新的动作类
- 没有需要了解所有内容的上帝对象工厂

问题
这种方法的问题在于,当与不同的客户端交谈时,我需要不同的URL,在这种情况下需要不同的URL,与Soap服务交谈需要调用正确的方法。 行动本身就是这些信息的守护者。但是当我深入挖掘时,这肯定会发生变化。

情景1#
我最终创建了结合动作和结果的类。所以我有类似&#34; HttpImport&#34;(基于HttpClient和ImportAction)的类。这导致 X(客户端)* Y(操作),现在总计8个类,这真的很糟糕。
情景2#
一些代码的时间!在这种情况下,即使我使用抽象,实现也会将我的类绑定在一起。

问题在于每个操作都需要为每个客户端提供一个属性(请记住它们访问不同的端点)。因此,如果我要再添加一个客户端,我将不得不完成所有操作并为该客户端端点添加另一个属性,以及添加另一个deocrator来删除对正确端点的所有调用(请记住我现在每个动作都有三个属性)。如果我要创建另一个动作,它就是那个动作。所以N *次操作+ 1(操作),在这种情况下5更改。好一点但仍然没有。

场景3#
这是上帝对象工厂。这里我们摆脱了持有端点的属性,并通过构造函数提供了enpoint。这将导致创建各种客户端和操作的方法。与上面的 X(客户端)* Y(操作)相同,如果要添加某些内容,则会将累积到工厂内的8个新方法中。工厂还必须保存端点信息。

代码
我的代码已演变为2:nd场景。我不想建造工厂,我很期待你们。

有些东西告诉我,客户端类做得很多,并且应该以某种方式与它们内部实现的类分离。

主要

static void Main(string[] args)
{
        IAction iact = new ImportAction();
        IDecorator idec = new HttpDecorator(iact);
        IClient icli = new HttpClient(idec);

        Console.Write(icli.connect().ToString());
        Console.ReadKey();
}

IAction

public interface IAction
{
    string[] Execute();
    string HttpString { get; }
    string SoapMethod { get; }
}

ImportAction

class ImportAction : IAction
{
    private string soapmethod;
    private string httpUrl;
    public ImportAction()
    {
        this.HttpString = @"http://www.hereiswereactionsgo.com";
    }   
    public string[] Execute()
    {   //Execute the action!
        return null;
    }
    public string HttpString { get; set; }
    public string SoapMethod { get; set; }
}

IDecorator

public interface IDecorator
{
    string GetActionString();
}

HttpDecorator

class HttpDecorator : IDecorator
{
    private IAction _action;
    public HttpDecorator(IAction action)
    {
        this._action = action;
    }
    public string GetActionString()
    {
        return _action.HttpString;
    }
    public string[] Execute()
    {
        throw new NotImplementedException();
    }
}

IClient

public interface IClient
{
    bool connect();
}

HttpClient的

class HttpClient : IClient
{
    private string _username;
    private string _password;
    private IDecorator _myaction;
    private HttpWebRequest webReq;

    public HttpClient(IDecorator action)
    {
        this._username = "myusername";
        this._password = "mypassword";
        this._myaction = action;   
    }

    public bool connect()
    {
        bool result = false;
        webReq = (HttpWebRequest)WebRequest.Create(_myaction.GetActionString());
        webReq.Credentials = new NetworkCredential(_username, _password);
        HttpWebResponse myHttpWebResponse = (HttpWebResponse)webReq.GetResponse();

        if (myHttpWebResponse.StatusCode == HttpStatusCode.OK)
        {
            result = true;
        }

        return result;
    }

}

1 个答案:

答案 0 :(得分:1)

访问者模式似乎适合此(Visitor)。 将操作视为访问者和客户端作为要访问的元素。将Action保持为抽象类而不是接口可能有助于提供样板代码。

  • 添加新动作范围BaseAction。实施getHttpUrl()getHttpBody()
  • 等方法
  • 要添加新客户端,需要更改现有类。您必须在每个Action类中实现相应的方法。我认为添加新客户端的频率会降低。

下面的示例代码遵循Java语法。

public static void main() {
  new HttpClient().performAction(new ImportAction());
}

public interface Client {
  performAction(Action);
}

public class HttpClient implements Client {
  public void accept(IAction a) {
    a.visitHttp(this);
  }
}

public abstract class Action {
  public visitHttp(HttpClient c) {
     getHttpUrl();
     c.connect(getHttpUrl());
     c.send(getHttpBody());
     c.close;
  }

  public visitSoap(SoapClient c) {

  }

  public abstract String getHttpUrl();
  public abstract String getHttpBody();
}

ImportAction extends Action {
  @Override
  getHttpUrl() {

  }

  @Override
  getHttpBody() {

  }
}