如何减少代码中的耦合度?

4

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

以下代码有点简化(没有指令和非常简单的逻辑),但思想是相同的。我有大约60个不同的翻译器,运行不同种类的业务逻辑。有些只需要一个参数就可以运行,而其他一些则需要多个参数。

我有一个抽象的翻译器类。该类的用户将使用两个公共方法: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语句来减少耦合。

附注:我限制使用.NET 3.5。


你是如何确定使用哪个翻译器来处理一行数据的? 你只是创建每种类型的翻译器并测试它是否能够进行翻译吗? - Mr.Mindor
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();

添加关于如何处理不同具体类型的不同输入的说明(因为它们期望不同的参数),这将是答案。 - Tengiz

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
{
    ...
}

0

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

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

如果我的理解是正确的,那么我认为您还没有找到模型的正确抽象级别。换句话说,您当前的抽象并没有帮助您(或多或少),因为它在每个具体类中都被扩展。因此,我的建议是首先查看模型本身(但这只是一个猜测,因为我不知道您的模型 :-))。

如果您能够提供7-10个代表性的具体类名称和它们所期望的输入值列表,那么这里的某个人可能会更好地指导您,以便让我们更好地了解您的领域。


这更像是一条评论而不是一个答案。 - Mr.Mindor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接