在不使工厂成为上帝对象的情况下组合两个模块
Combining two modules without making the factory a god object
我们有一个联系多个不同远程服务(SOAP、HTTPREQUEST)的应用程序。然后我们执行不同的操作(导入、导出、更新、删除)。
今天我们有两个客户classes和四个动作classes。
问题!
我如何解耦这两个模块,以便我必须做最少的更改。 IE 只添加一个新的 action/new 客户端。仅此而已。
客户端class
授权我们的客户端使用远程服务,它处理登录和注销。
操作class
持有 url 方法来调用客户端。以及 ExecuteActionMethod
用法
客户端 class 得到一个动作装饰,然后与客户端一起执行该动作。
恐惧
我不想:
- 每次我添加一个新的 客户端 class[=101= 时创建一个新的 操作 class ]
- 创建一个新的客户端class 每次我添加一个新的action class
- 没有需要知道一切的上帝对象工厂
问题
这种方法的问题是,当与不同的客户交谈时,我需要不同的信息,在这种情况下,不同的 URL,与 Soap 服务交谈需要调用正确的方法。 操作本身就是此信息的保管人。但随着我深入挖掘,这肯定会发生变化。
场景一#
我最终创建了 classes 结合了行动和结果。所以我有 class 类 "HttpImport"(基于 HttpClient 和 ImportAction)。这导致 X(Clients) * Y(Actions) 现在 总数为 8 classes,这真的很糟糕。
场景2#
是时候写一些代码了!在这种情况下,实现将我的 classes 绑定在一起,即使我使用抽象。
这里的问题是每个动作都需要为每个客户端(记住它们访问不同的端点)有一个 属性。因此,如果我要再添加一个 client,我将不得不完成所有操作并为该客户端端点添加另一个 属性,以及添加另一个 deocrator 以删除所有调用到正确的端点(记住我现在在每个动作中都有三个属性)。如果我要创建另一个动作,那就是那个动作。所以 N* 次动作 + 1(动作),在这种情况下有 5 次变化。好一点了,但还是不行。
场景3#
这是上帝对象工厂。在这里,我们摆脱了持有端点的属性,并通过构造函数提供了端点。这将产生用于创建各种客户端和操作的方法。同上 X(Clients) * Y(Actions) 如果要添加一些东西,这些在 factory 中累积 为 8 个新方法。工厂还必须保存端点信息。
代码
我的代码已经发展到 2:nd 场景。我不想建工厂,我在找你们。
有些事情告诉我,客户端 classes 做了很多事情,应该以某种方式与它们在内部实例化的 classes 分离。
主要
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; }
}
导入操作
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;
}
}
Visitor 模式似乎适合这个 (Visitor) 。
将动作视为访问者,将客户视为要访问的元素。将 Action 保持为抽象 class 而不是接口可能有助于提供样板代码。
- 要添加新的操作扩展
BaseAction
。实现getHttpUrl()
、getHttpBody()
等方法
- 要添加新客户端,需要更改现有的 classes。您必须在每个 Action class 中实现相应的方法。我假设添加新客户的频率会降低。
下面的示例代码遵循 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() {
}
}
我们有一个联系多个不同远程服务(SOAP、HTTPREQUEST)的应用程序。然后我们执行不同的操作(导入、导出、更新、删除)。
今天我们有两个客户classes和四个动作classes。
问题!
我如何解耦这两个模块,以便我必须做最少的更改。 IE 只添加一个新的 action/new 客户端。仅此而已。
客户端class
授权我们的客户端使用远程服务,它处理登录和注销。
操作class
持有 url 方法来调用客户端。以及 ExecuteActionMethod
用法
客户端 class 得到一个动作装饰,然后与客户端一起执行该动作。
恐惧
我不想:
- 每次我添加一个新的 客户端 class[=101= 时创建一个新的 操作 class ]
- 创建一个新的客户端class 每次我添加一个新的action class
- 没有需要知道一切的上帝对象工厂
问题
这种方法的问题是,当与不同的客户交谈时,我需要不同的信息,在这种情况下,不同的 URL,与 Soap 服务交谈需要调用正确的方法。 操作本身就是此信息的保管人。但随着我深入挖掘,这肯定会发生变化。
场景一#
我最终创建了 classes 结合了行动和结果。所以我有 class 类 "HttpImport"(基于 HttpClient 和 ImportAction)。这导致 X(Clients) * Y(Actions) 现在 总数为 8 classes,这真的很糟糕。
场景2#
是时候写一些代码了!在这种情况下,实现将我的 classes 绑定在一起,即使我使用抽象。
这里的问题是每个动作都需要为每个客户端(记住它们访问不同的端点)有一个 属性。因此,如果我要再添加一个 client,我将不得不完成所有操作并为该客户端端点添加另一个 属性,以及添加另一个 deocrator 以删除所有调用到正确的端点(记住我现在在每个动作中都有三个属性)。如果我要创建另一个动作,那就是那个动作。所以 N* 次动作 + 1(动作),在这种情况下有 5 次变化。好一点了,但还是不行。
场景3#
这是上帝对象工厂。在这里,我们摆脱了持有端点的属性,并通过构造函数提供了端点。这将产生用于创建各种客户端和操作的方法。同上 X(Clients) * Y(Actions) 如果要添加一些东西,这些在 factory 中累积 为 8 个新方法。工厂还必须保存端点信息。
代码
我的代码已经发展到 2:nd 场景。我不想建工厂,我在找你们。
有些事情告诉我,客户端 classes 做了很多事情,应该以某种方式与它们在内部实例化的 classes 分离。
主要
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; }
}
导入操作
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;
}
}
Visitor 模式似乎适合这个 (Visitor) 。 将动作视为访问者,将客户视为要访问的元素。将 Action 保持为抽象 class 而不是接口可能有助于提供样板代码。
- 要添加新的操作扩展
BaseAction
。实现getHttpUrl()
、getHttpBody()
等方法 - 要添加新客户端,需要更改现有的 classes。您必须在每个 Action class 中实现相应的方法。我假设添加新客户的频率会降低。
下面的示例代码遵循 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() {
}
}