你会如何改进这个简单的类以更松散地耦合?

时间:2022-12-25 03:52:27

As you can see below, in the constructor I'm instantiating a validation object so I can validate a user's email in a set method. Is this architecture best practice or flawed? Can I avoid making my User class directly dependent on my Validation class?

正如您在下面看到的,在构造函数中我实例化了一个验证对象,因此我可以在set方法中验证用户的电子邮件。这种架构是最佳实践还是有缺陷的?我可以避免让我的User类直接依赖于我的Validation类吗?

Class User {
  Private Email

//constructor
User() {
  Validation = new Validation
}

SetEmail(NewValue) {
  if (Validation.isEmail(NewValue)) {
    Email = NewValue
  }
}

And a related question: When a set method receives an invalid value, what is the proper response? I see 2 options

还有一个相关问题:当​​set方法收到无效值时,什么是正确的响应?我看到2个选项

  1. Don't set the value and return false
  2. 不要设置值并返回false

  3. Set the value anyway, but set an error property for the object. (so if User.Error is set I know something went wrong)
  4. 无论如何设置值,但为对象设置error属性。 (如果设置了User.Error,我知道出了问题)

I suspect #1 is best practice because you can then assure the value of any object property is always valid. Correct?

我怀疑#1是最佳实践,因为您可以确保任何对象属性的值始终有效。正确?

2 个解决方案

#1


10  

The proposals so far all seem to be way overkill, especially with all the IOC and AOP stuff.

到目前为止,这些建议似乎都有点过分,特别是对于所有IOC和AOP的东西。

  1. The User class needs an email address, so create an EmailAddress class and have the User class accept one via a property and/or its constructor. That validation can be as simple as whether the input EmailAddress reference is null or not.

    User类需要一个电子邮件地址,因此创建一个EmailAddress类并让User类通过属性和/或其构造函数接受一个。该验证可以像输入EmailAddress引用是否为null一样简单。

  2. The EmailAddress class can be a simple but generally reusable implementation (consider basing it on the RFC document). It should be immutable and should throw an exception from its constructor on invalid input.

    EmailAddress类可以是一个简单但通常可重用的实现(考虑将其基于RFC文档)。它应该是不可变的,并且应该在无效输入上从其构造函数中抛出异常。

  3. Ideally, the EmailAddress class should be composed of an EmailUserId class (based on the RFC?) and a InternetDomain class (based on the RFC?), since an email address is a composite data structure. Again, each of those classes should manage immutable instances and should throw an exception on construction with invalid input.

    理想情况下,EmailAddress类应该由EmailUserId类(基于RFC?)和InternetDomain类(基于RFC?)组成,因为电子邮件地址是复合数据结构。同样,这些类中的每一个都应该管理不可变实例,并且应该在构造时使用无效输入抛出异常。

"Validation" strikes me as not a "thing" but rather a generic "action". Therefore, it lends itself to being a method rather than a class. In this case, I tend to implement the validation in each of these classes as a private static method (valid(input)) that is invoked from the constructor, in languages like Java or C#. Often, it becomes useful to expose that functionality publicly in the form of a question (isValid(input)).

“验证”让我觉得不是“事物”,而是一种通用的“行动”。因此,它有助于成为一种方法而不是一种类。在这种情况下,我倾向于在每个类中实现验证,作为从构造函数调用的私有静态方法(有效(输入)),使用Java或C#等语言。通常,以问题的形式公开该功能变得有用(isValid(输入))。

EDIT:

Are you suggesting that every distinct data type I need to validate should have it's own class?

您是否建议我需要验证的每种不同数据类型都应该拥有自己的类?

That is one solid way of addressing the issue, commonly known as a Value Type (thanks for the reminder, Frank). The result will be a few (dozen or two) well-defined, reusable classes like perhaps EmailAddress, PhoneNumber, PersonName, etc. The presented alternative is likely to result in a "god class" with a mixture of functionality that is not reusable, not easy to test, and difficult to maintain.

这是解决问题的一种可靠方式,通常称为价值类型(感谢提醒,弗兰克)。结果将是一些(十二个或两个)明确定义的,可重用的类,如EmailAddress,PhoneNumber,PersonName等。所提出的替代方案可能导致具有不可重用的功能混合的“上帝类”,不容易测试,难以维护。

There are other ways to partition the solution, but my suggestion does have the advantage of being mature, well understood, and consistent with a large set of solid design principles. I would certainly recommend trying it before attempting to invent your own.

还有其他方法可以对解决方案进行分区,但我的建议确实具有成熟,易于理解和与大量可靠设计原则相一致的优势。我肯定会在尝试发明你自己之前尝试一下。

#2


1  

I would:

  1. Break the coupling to the concrete Validation object via dependency injection: define an abstract (pure virtual) Validation class, make a concrete validation class derive from it, and pass in ("inject") a reference to the abstract Validation class in the User class's constructor.

    通过依赖注入打破与具体Validation对象的耦合:定义一个抽象(纯虚拟)验证类,从中派生一个具体的验证类,并传入(“注入”)对User类的抽象Validation类的引用构造函数。

    For an excellent discussion of how and why to do this in C++, see Robert Martin's 1996 article on the subject from The C++ Report.

    有关如何以及为何在C ++中执行此操作的出色讨论,请参阅Robert Martin 1996年关于C ++报告主题的文章。

  2. Rather than returning false or silently setting some property, raise an exception. That's what they are there for.

    不是返回false或静默设置某个属性,而是引发异常。这就是他们的目的。

#1


10  

The proposals so far all seem to be way overkill, especially with all the IOC and AOP stuff.

到目前为止,这些建议似乎都有点过分,特别是对于所有IOC和AOP的东西。

  1. The User class needs an email address, so create an EmailAddress class and have the User class accept one via a property and/or its constructor. That validation can be as simple as whether the input EmailAddress reference is null or not.

    User类需要一个电子邮件地址,因此创建一个EmailAddress类并让User类通过属性和/或其构造函数接受一个。该验证可以像输入EmailAddress引用是否为null一样简单。

  2. The EmailAddress class can be a simple but generally reusable implementation (consider basing it on the RFC document). It should be immutable and should throw an exception from its constructor on invalid input.

    EmailAddress类可以是一个简单但通常可重用的实现(考虑将其基于RFC文档)。它应该是不可变的,并且应该在无效输入上从其构造函数中抛出异常。

  3. Ideally, the EmailAddress class should be composed of an EmailUserId class (based on the RFC?) and a InternetDomain class (based on the RFC?), since an email address is a composite data structure. Again, each of those classes should manage immutable instances and should throw an exception on construction with invalid input.

    理想情况下,EmailAddress类应该由EmailUserId类(基于RFC?)和InternetDomain类(基于RFC?)组成,因为电子邮件地址是复合数据结构。同样,这些类中的每一个都应该管理不可变实例,并且应该在构造时使用无效输入抛出异常。

"Validation" strikes me as not a "thing" but rather a generic "action". Therefore, it lends itself to being a method rather than a class. In this case, I tend to implement the validation in each of these classes as a private static method (valid(input)) that is invoked from the constructor, in languages like Java or C#. Often, it becomes useful to expose that functionality publicly in the form of a question (isValid(input)).

“验证”让我觉得不是“事物”,而是一种通用的“行动”。因此,它有助于成为一种方法而不是一种类。在这种情况下,我倾向于在每个类中实现验证,作为从构造函数调用的私有静态方法(有效(输入)),使用Java或C#等语言。通常,以问题的形式公开该功能变得有用(isValid(输入))。

EDIT:

Are you suggesting that every distinct data type I need to validate should have it's own class?

您是否建议我需要验证的每种不同数据类型都应该拥有自己的类?

That is one solid way of addressing the issue, commonly known as a Value Type (thanks for the reminder, Frank). The result will be a few (dozen or two) well-defined, reusable classes like perhaps EmailAddress, PhoneNumber, PersonName, etc. The presented alternative is likely to result in a "god class" with a mixture of functionality that is not reusable, not easy to test, and difficult to maintain.

这是解决问题的一种可靠方式,通常称为价值类型(感谢提醒,弗兰克)。结果将是一些(十二个或两个)明确定义的,可重用的类,如EmailAddress,PhoneNumber,PersonName等。所提出的替代方案可能导致具有不可重用的功能混合的“上帝类”,不容易测试,难以维护。

There are other ways to partition the solution, but my suggestion does have the advantage of being mature, well understood, and consistent with a large set of solid design principles. I would certainly recommend trying it before attempting to invent your own.

还有其他方法可以对解决方案进行分区,但我的建议确实具有成熟,易于理解和与大量可靠设计原则相一致的优势。我肯定会在尝试发明你自己之前尝试一下。

#2


1  

I would:

  1. Break the coupling to the concrete Validation object via dependency injection: define an abstract (pure virtual) Validation class, make a concrete validation class derive from it, and pass in ("inject") a reference to the abstract Validation class in the User class's constructor.

    通过依赖注入打破与具体Validation对象的耦合:定义一个抽象(纯虚拟)验证类,从中派生一个具体的验证类,并传入(“注入”)对User类的抽象Validation类的引用构造函数。

    For an excellent discussion of how and why to do this in C++, see Robert Martin's 1996 article on the subject from The C++ Report.

    有关如何以及为何在C ++中执行此操作的出色讨论,请参阅Robert Martin 1996年关于C ++报告主题的文章。

  2. Rather than returning false or silently setting some property, raise an exception. That's what they are there for.

    不是返回false或静默设置某个属性,而是引发异常。这就是他们的目的。