软件设计原则:将 collection 与 object 分开?
Software Design Principle: Seperate your collection from working on an object?
在OOP中与一个小组合作设计一个小组成员,我有一个分歧,我在网上找不到答案。
创建一个类似于非营利组织的软件,该组织的组织有捐助者及其信用卡。
一种方法是 creditCardList
(collection class)应该只存储 return creditCard
object。然后,当它们被 returned 时,Organization
class 应该 运行 processCard()
对于每个 object 作为 creditCard
object。
另一方面是 CreditCardList
应该处理所有内容并包含 运行 遍历 collection 中所有项目的循环。
一般来说,什么是更好的软件设计?
我会选择第一个选项
由于您已经将 CreditCardList
用作存储,因此 class 不应承担任何其他责任。 class 的功能仅限于提供卡片(fully/partially,符合 condition/by 标准)。
第二种方案提出了存储与卡处理机制耦合的问题。您的存储库现在取决于处理系统以及系统可能需要的输入参数。此外,存储确实知道它不应该知道的所有进程。
抱歉这么说,但我不会选择这两种方法来实现职责分离原则。如果我对要求的理解正确,那么 Organization 对象是代表接受捐赠的慈善组织的实体。 CreditCardList 是用于支付处理的所有信用卡的集合。它们都不是保存支付处理逻辑的好地方。
相反,我会介绍一个新的助手 class,其名称类似于 PaymentProcessor
。这个 class 将有一个名为 processPayment(List<CreditCard> creditCards)
的方法。此方法应包含处理信用卡付款的逻辑。通过这种设计,您还可以在此 class 中引入其他方法,以使用其他方式处理付款,例如直接从银行帐户或 PayPal 借记。
根据 OO 设计原则,我们应该有一个 class 或接口,仅用于一个目的,以提高可维护性。
我希望这可能有助于解决您与同事之间的建设性分歧。 :)
首先,我发现你图中的 for-each 调用很奇怪:为什么组织调用方法 CreditCard.processCard 并将 creditCard 实例作为参数传递?
"processing a credit card" 首先是什么意思?信用卡实例能处理吗?
我要做出的第一个设计决定是 CreditCard 对象不应依赖于某些外部服务。处理信用卡听起来像是处理外部事物。
我会保持 CreditCard 逻辑的纯净。只将对局部字段和属性进行操作的逻辑放在此处。
CreditCardList 有一项主要任务。持有一套信用卡。您想要检索和存储信用卡列表的方式可能会发生变化,因此最好将关注点分开并将处理逻辑排除在外class。
运行 CreditCardList 中循环的原因之一可能是您不想公开具体的信用卡列表。但这可以用更优雅的方式处理。定义接口 ICardProcessor 并将其作为构造函数参数注入到 CreditCardList 中。
然后 foreach 可以保留在 class 内并在每个实例上调用 ICardProcessor.Process(card)。
这是另一种设计思路。让我们先对问题建模,然后使用问题的域来为事物命名,而不是关注完成目标所必需的步骤。
显然有Organization
s,它接受Donation
s。好的,让我们试试这个:
public interface Organization {
void accept(Donation donation);
}
public interface Donation {
}
现在,捐赠的钱可能需要在 accept()
方法中的某个时刻转移到组织,所以让我们将其添加为 Donation
对象的职责:
public interface Donation {
void transferTo(BankReference target);
}
在示例中,您一次性处理了多项捐赠。这是您询问的迭代。这将在 class:
public final class Donations implements Donation {
...
public Donations(Donation... donations) {
...
}
@Override
public void transferTo(BankReference target) {
donations.forEach(donation -> donation.transferTo(target));
}
}
接下来,您想要使用信用卡进行捐赠。您可以通过提供特定的捐赠类型来做到这一点。
public final class CreditCardDonation implements Donation {
...
@Override
public void transferTo(BankReference target) {
// Here is the credit card processing logic
}
}
我显然不知道您要求的所有细节,但我的意思是从您拥有的东西开始,而不是技术步骤(如信用卡处理)这需要发生。这也适用于方法名称,而不仅仅是 class 名称!
在OOP中与一个小组合作设计一个小组成员,我有一个分歧,我在网上找不到答案。
创建一个类似于非营利组织的软件,该组织的组织有捐助者及其信用卡。
一种方法是 creditCardList
(collection class)应该只存储 return creditCard
object。然后,当它们被 returned 时,Organization
class 应该 运行 processCard()
对于每个 object 作为 creditCard
object。
另一方面是 CreditCardList
应该处理所有内容并包含 运行 遍历 collection 中所有项目的循环。
一般来说,什么是更好的软件设计?
我会选择第一个选项
由于您已经将 CreditCardList
用作存储,因此 class 不应承担任何其他责任。 class 的功能仅限于提供卡片(fully/partially,符合 condition/by 标准)。
第二种方案提出了存储与卡处理机制耦合的问题。您的存储库现在取决于处理系统以及系统可能需要的输入参数。此外,存储确实知道它不应该知道的所有进程。
抱歉这么说,但我不会选择这两种方法来实现职责分离原则。如果我对要求的理解正确,那么 Organization 对象是代表接受捐赠的慈善组织的实体。 CreditCardList 是用于支付处理的所有信用卡的集合。它们都不是保存支付处理逻辑的好地方。
相反,我会介绍一个新的助手 class,其名称类似于 PaymentProcessor
。这个 class 将有一个名为 processPayment(List<CreditCard> creditCards)
的方法。此方法应包含处理信用卡付款的逻辑。通过这种设计,您还可以在此 class 中引入其他方法,以使用其他方式处理付款,例如直接从银行帐户或 PayPal 借记。
根据 OO 设计原则,我们应该有一个 class 或接口,仅用于一个目的,以提高可维护性。
我希望这可能有助于解决您与同事之间的建设性分歧。 :)
首先,我发现你图中的 for-each 调用很奇怪:为什么组织调用方法 CreditCard.processCard 并将 creditCard 实例作为参数传递?
"processing a credit card" 首先是什么意思?信用卡实例能处理吗? 我要做出的第一个设计决定是 CreditCard 对象不应依赖于某些外部服务。处理信用卡听起来像是处理外部事物。 我会保持 CreditCard 逻辑的纯净。只将对局部字段和属性进行操作的逻辑放在此处。
CreditCardList 有一项主要任务。持有一套信用卡。您想要检索和存储信用卡列表的方式可能会发生变化,因此最好将关注点分开并将处理逻辑排除在外class。 运行 CreditCardList 中循环的原因之一可能是您不想公开具体的信用卡列表。但这可以用更优雅的方式处理。定义接口 ICardProcessor 并将其作为构造函数参数注入到 CreditCardList 中。 然后 foreach 可以保留在 class 内并在每个实例上调用 ICardProcessor.Process(card)。
这是另一种设计思路。让我们先对问题建模,然后使用问题的域来为事物命名,而不是关注完成目标所必需的步骤。
显然有Organization
s,它接受Donation
s。好的,让我们试试这个:
public interface Organization {
void accept(Donation donation);
}
public interface Donation {
}
现在,捐赠的钱可能需要在 accept()
方法中的某个时刻转移到组织,所以让我们将其添加为 Donation
对象的职责:
public interface Donation {
void transferTo(BankReference target);
}
在示例中,您一次性处理了多项捐赠。这是您询问的迭代。这将在 class:
public final class Donations implements Donation {
...
public Donations(Donation... donations) {
...
}
@Override
public void transferTo(BankReference target) {
donations.forEach(donation -> donation.transferTo(target));
}
}
接下来,您想要使用信用卡进行捐赠。您可以通过提供特定的捐赠类型来做到这一点。
public final class CreditCardDonation implements Donation {
...
@Override
public void transferTo(BankReference target) {
// Here is the credit card processing logic
}
}
我显然不知道您要求的所有细节,但我的意思是从您拥有的东西开始,而不是技术步骤(如信用卡处理)这需要发生。这也适用于方法名称,而不仅仅是 class 名称!