4

在工作中,我正在做这个应用程序,它从外部文件(Excel 工作表、文本文件……)中获取值,并将这些值转换为复杂的指令,然后输入另一个系统。

下面的代码有点简化(没有指令和非常简单的逻辑),但想法保持不变。我有大约 60 名不同的翻译器,它们背后运行着不同类型的业务逻辑。有些只需要一个参数即可运行。其他人则采用多个论点。

我有一个抽象翻译类。该类的用户将使用 2 个公共方法:Translate 运行翻译逻辑,CanTranslate 允许查看翻译器是否准备好启动。

使用此抽象类的开发人员需要实现包含实际业务逻辑的 DoTranslate 方法。默认情况下,CanTranslate 始终返回 true,但如果需要验证,则可以覆盖它。

这是抽象翻译器基类:

// Contains some base logic which is the same for all translators
public abstract class BaseTranslator
{
    // Public translate method
    public void Translate()
    {
        if (CanTranslate())
            DoTranslate();
    }

    // Checks if we are ready to translate
    // True by default
    public virtual bool CanTranslate()
    {
        return true;
    }

    // This method is used to implement business logic
    public abstract void DoTranslate();
}

这是一个具体的翻译类的实现:

// Translates beer names
public class ReverseTranslator : BaseTranslator
{
    // Use of properties to allow strongly typed arguments
    // which can be seen by the developer at design time
    public string BeerName { get; set; }

    // Validation
    public override bool CanTranslate()
    {
        if (BeerName.Equals("Budweiser") || BeerName.Equals("Stella"))
            return true;
        else
            return false;
    }

    // Implementation of the business logic
    public override void DoTranslate()
    {
        char[] letters = BeerName.ToCharArray();
        Array.Reverse(letters);
        Console.WriteLine(new string(letters));
    }
}

这是使用时的样子:

class Program
{
    public static void Main(string[] args)
    {
        var translator = new ReverseTranslator();

        translator.BeerName = "Stella";
        translator.Translate();

        translator.BeerName = "I'm not a beer";
        // This line will not translate since it's not a valid beer name.
        translator.Translate();

        Console.ReadLine();
    }
}

专业人士:

  • 在小的可维护单元中分离特定的业务逻辑
  • 翻译器很容易在应用程序的其他部分重用
  • 翻译器可以很容易地进行单元测试
  • 属性允许翻译器的用户查看需要哪些参数

我的问题:

  • 不同的控制器类正在使用许多翻译器。我有太多的耦合。

我曾想过使用工厂模式来创建翻译器,但后来我不能在设计时使用属性作为参数提示。

所以我基本上是在寻找一种解决方案,在设计期间你可以很容易地看到需要哪些参数。同时我希望通过不让每个控制器都有 30 个新的 xTranslator 语句来减少耦合。

PS:我仅限于此代码使用 .NET 3.5。

4

3 回答 3

2
different controller classes are using many translators. I have too much coupling

控制器类应该只依赖于抽象,即BaseTranslator. 因此你不会有太多的耦合,实际上它会是松耦合的代码。通过依赖注入(例如通过构造函数参数)将依赖注入到您的控制器中。

使您的代码仅依赖于基类型的一种选择 -Text在基类中创建字符串属性:

BaseTranslator translator = new ReverseTranslator(); // inject implementation
translator.Text = "Stella";
translator.Translate();
translator.Text = "I'm not a beer";
translator.Translate();
于 2012-11-27T21:54:25.543 回答
0

我突然想到有一些矛盾的地方:

  • 一方面,翻译作者可以定义自己的具体类(并添加新的“强制”属性,这些属性必须具有值才能使具体类正常工作)
  • 另一方面,您依赖于其他人在代码中定义的新具体类,因为您是实例化类并向它们传递正确参数的人

如果这是正确的,我相信您还没有为您的模型找到合适的抽象级别。换句话说,您当前的抽象对您根本没有帮助(或多或少),因为它在每个具体类中都得到了扩展。所以我的建议是先看看模型本身(但这只是一个猜测,因为我不知道你的模型:-))。

如果您能够提供一个包含 7-10 个具有代表性的具体类名的列表以及他们希望我们更好地了解您的域的输入值,那么这里的某个人可能会更好地指导您。

于 2012-11-27T22:17:07.833 回答
0

要获得具体类的特定参数的设计时信息: 您需要使用具体类的实例
为了减少控制器中的耦合: 您需要限制自己的抽象类实例
你不能同时在同一个地方。

重新设计您的整体设计,您可以消除耦合并消除对设计时间信息的需求。

将转换器的创建和初始化从控制器移到工厂或 IoC 容器中,该容器将外部文件中的一行数据作为输入(如果需要,将其按摩成可以使用的格式)。

让翻译人员需要一个带有参数集合的构造函数。
好处:

  • 只有具体类本身需要知道它自己或其参数的细节。
  • 工厂需要有关在哪种情况下使用哪个翻译器的信息。

.

class TranslatorFactory
{
    //translator lookup table
    private Dictionary<string, Func<List<string>,BaseTranslator>> Translators = 
        new Dictionary<string,Func<List<string>,BaseTranslator>>{
            {"Reverse", (args)=>new ReverseTranslator(args)},
            {"Explode", (args)=>new ExplodeTranslator(args)}        };

    public BaseTranslator GetTranslatorForRow(string command, List<string> arguments)
    {
        if(Translators.ContainsKey(command) )
        {
             return Translators[command](arguments);
        }
        return null; //or default, or throw exception
    }
}


abstract class BaseTranslator
{
    ...
    public BaseTranslator(List<string> args)
    {
    }
}

class ReverseTranslator: BaseTranslator
{
    public string BeerName {get;set;}
    public ReverseTranslator(List<string> args)
    {
        BeerName = args[0];
    }
}

您可以更进一步,使用属性和反射来消除工厂和具体类之间的耦合,以动态构建查找表。

[TranslatorFor("Reverse")]
class ReverseTranslator: BaseTranslator
{
    ...
}
于 2012-11-28T00:09:44.740 回答