7

刚从大学毕业,遇到了一些我需要减少耦合的代码。但我并不完全理解所有的概念,并且想要一个简单的例子来帮助我。为了让你开始,我有一个带有单个字段的人员类,名称。我在该类中有一个方法来连接一些文本。

我知道这是一个愚蠢的例子,大多数人永远不会考虑在如此简单的情况下减少耦合,但我只想要一个简单的例子来帮助我完全理解代码和概念。

在主窗口后面的代码中,我放置了一个文本框和一个按钮。当窗口加载时,它会显示人员 x 姓名字段的当前值。单击按钮时,将调用 x.PersonAddText 方法。目前,此示例的耦合计算为 8。按钮单击事件为 3,窗口加载事件为 3。

有什么办法吗,使用这个例子,我们可以将它们中的一个或两个都降低到低于这个值。

以下是我的所有代码:

我的个人类:

public class Person
{
    //Fields
    private string name;

    //Properties
    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    //Constructors
    public Person()
    {
        name = "joe";
    }

    //Methods
    public string PersonAddText(string text)
    {
        return name += " - " + text;
    }

    //Interfaces (or additional code below here please to aid understanding)
}

我的代码背后:

    Person x = new Person();

    public MainWindow()
    {
        InitializeComponent();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        txtname.Text = x.Name;
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        txtname.Text = x.PersonAddText(txtname.Text);
        txtname.Text = x.Name;
    }

我的简单 XAML:

<Grid>
    <TextBox Name="txtname" Margin="12,12,12,0" Height="23" VerticalAlignment="Top" />
    <Button Content="Add Text" Margin="12,41,12,0" Name="button1" VerticalAlignment="Top" Click="button1_Click" />
</Grid>

我很难理解互联网上解释这一点的教程。据我所知,有 3 种方法可以做到这一点(如果可能的话,将上面的代码转换为所有这三种方法的示例会很好):

  • 服务定位器
  • 依赖注入
  • 控制反转 (IoC)

解释我读过的东西的文章非常好,但这些例子与我无关,因为他正在使用带有数据库连接字符串的 VB 和 ASP.Net。这与我所需要的完全相反,我不想在学习概念的同时考虑如何翻译代码,也不想考虑如何将其应用到相关的东西上。虽然这个例子很好,但它太多了,我真的很感激任何额外的帮助。

编辑历史:更正拼写。添加以下内容以澄清我的问题:

我理解耦合和内聚背后的理论,以及为什么你应该减少一个并增加另一个。但是我们在大学里从来没有编写过任何例子。另外,虽然在大学里没有涉及,但我确实了解接口。但是,我不明白如何使用它们来减少耦合。

添加了我上面引用的文章的链接。

编辑2:到目前为止,我现在得到的是以下内容:

public interface IPerson 
{ 
    string Name { get; set; } 
    string PersonAddText(string text); 
} 

public class Person : IPerson 
{
    //The code from the person class above
}

我现在如何在后面的主窗口代码中使用它?我想我应该更换

Person x = new Person();

IPerson x = new Person(); 

这是正确的吗?如果是,我还需要做什么。我问的原因是因为我仍然没有看到 Visual Studio 报告的代码耦合数字有任何减少(事实上,它在后面的主窗口代码上增加了 1)。

4

3 回答 3

3

编辑

我很高兴我的回答有所帮助,让我进一步更新一下。要将您的问题用作直接答案,您需要更改的只是您的字段声明:

Person x = new Person();

IPerson x = new Person();

您的代码隐藏现在知道在您的接口中指定的属性和方法,并且耦合少了很多,因为您可以new Person()new Student()以后换掉。只要对象实现了接口。您的代码隐藏现在应该可以在没有任何必要更改的情况下工作。

边注

我建议考虑延迟加载此x人,并使用名称更容易识别的属性。注意,这并不能回答您的问题,而只是一个建议。:)

private IPerson _CurrentPerson = null;
private IPerson CurrentPerson
{
    get
    {
        if (this._CurrentPerson == null)
        {
            this._CurrentPerson = new Person();
        }
        return this._CurrentPerson
    }
    set
    {
        this._CurrentPerson = value;
    }
}

解耦是指两个或更多代码块不应相互依赖。控制反转是指对象的耦合在运行时绑定,从而允许对象及其实例具有更大的灵活性,从而减少耦合。控制反转最好与接口结合使用。接口定义了ClassA将做MethodX和拥有PropertyY. 我们的主要对象并不关心在运行时返回什么对象,因为它可以实现接口,它很高兴。

在上面的示例中,您将希望连接您的人员类,可能是这样的:

public interface IPerson
{
    string Name { get; set; }
    string PersonAddText(string text);
}

public class Person : IPerson
{
    // your code here
}

然后,在您的主要方法调用中Person,您将使用实现接口的对象实例,而不是显式使用对象IPerson。接口和对象的“连接”可以通过各种不同的库来实现,这将有助于设置您的依赖关系。根据我的经验,我使用过StructureMapMicrosoft 的 Enterprise Library。它们的设置可能有点繁琐,但是一旦它们设置好了,你就可以做这样的事情......

public void MainMethod_InInterfaceLayer()
{
    // container is an instance of UnityContainer
    Person newPerson = container.Resolve<IPerson>();
}

我知道这不是一个完整的答案,但希望它会有所帮助。:)

于 2012-05-09T11:11:58.387 回答
2

假设您有一个IPerson接口和几个实现(Person,等) StudentTeacher并且您有一些只需要在IPerson.

有:

IPerson x = new Person();

在你的代码后面强烈地将它与Person类耦合。

控制反转通过让依赖项来自外部而不是在类内部创建来发挥作用。

这通常是通过使用依赖注入来实现的——IPerson传递给类而不是直接创建它的类。您可以通过将实例传递给构造函数(构造函数注入)或作为属性(属性注入)来实现。

服务定位器是另一种无需硬编码即可获得依赖关系的方法——该模式“定位”类型/接口的实例。

如何将依赖项注入类的示例:

public class MyClass
{
  IPerson person;

  public MyClass(IPerson p)
  {
    person = p; // injected an instance of IPerson to MyClass
  }

  // can now use person in any method of the class, or pass it around:

  public void MyMethod()
  {
     string name = person.Name;
  }
}
于 2012-05-09T11:15:25.887 回答
0

假设您有一个 Person 类,它可以从数据库中加载自身、进行更改并在他或她去世时发送电子邮件。

class Person 
{
    ...
    void LoadUsingId(int id);
    void HaveDiedWillMail();
    void SetFirstName(string name);
    ...
}

你在这里可以看到这个人实际上在做三件事(至少!)。

它知道如何与数据库对话以加载自身。它知道如何发送电子邮件。它可以改变自己。

任何代码,理论上如果不实践,应该只对一件事负责。为了减少这些组件之间的耦合,您必须将它们分开。将它们解耦,以便它们可以独立工作。

class Person 
{
    ...
    void LoadUsingId(PersonRepository person);
    void HaveDiedWillMail(IMailer mailer);
    void SetFirstName(string name);
    ...
}

在这个人为的示例中,Person 不知道如何从数据库中加载某些内容。忽略 -loading- 也是您应该解耦的事实。邮件发送也被解耦了,因为你告诉Person.HaveDiedWillMail你你想使用一个特定的邮件程序 ( IMailer mailer)。

依赖注入、服务容器和此类技术会自动找到您的 Person 需要/想要运行的组件,这是在解耦之上的额外层,以便更容易地将所有单独的部分连接在一起。

于 2012-05-09T11:15:44.197 回答