在这种情况下使用“数据类”是个坏主意吗?

时间:2014-06-30 14:14:56

标签: java design-patterns anti-patterns

我目前正在使用您在工作中称为遗留代码的内容。我基本上有一个处理工作流的类,每次启动机器时都只实例化一次。每次新用户使用此计算机并登录到该计算机时,新的"会话"已启动,但我们保留此类的相同实例。为了简单起见,我们可以说这台机器是一台自助服务机器。

这基本上意味着每次新用户会话开始时,我们都需要将所有流变量重置为默认值。

通常我会有实例/成员变量,例如:

boolean didUserPressYes;
boolean didUserTakeHisReceipt;
int numberOfInsertedItems;

然后是在流/用户会话开始时调用的resetVars()方法:

public void resetVars() {
 didUserPressYes = false;
 didUserTakeHisReceipt = false;
 numberOfInsertedItems = 0;
}

通常我只是重新实例化这个类并避免使用这个resetVars()方法。不幸的是,我们与一个不允许我们这样做的框架联系在一起,我们必须保持同一个类的实例。

在我们的实际代码中,我们有大约12个这些变量,并且将来几乎肯定会增加更多。我可以看到在每个用户会话启动时这些变量未正确重置为其默认值的错误的巨大潜力 - 特别是如果有新的人进入项目并忘记将新变量添加到此resetVars()方法中。

要解决此问题,我已经创建了一个包含这些变量的WorkflowVariables类,并且具有适当的getter和setter,以及一些小的实用程序方法。在每个用户会话开始时,我们只需重新实例化此WorkflowVariables类,现在可以知道所有变量都已恢复为默认值的事实。无需担心某些实例变量没有被重置,因为我们只有一个:

private WorkFlowVariables workFlowVariables;

public void resetVars() {
    workFlowVariables = new WorkFlowVariables();
}

然而,经过一些"代码味道"阅读,似乎这就是所谓的数据类"并且是不好的实践。

有人认为这在我的情况下是一个坏主意,还是适合这种特定情况?也许有更好的方法来解决这个问题?如果我们保留多个实例变量,我是否太担心错误的可能性?

提前致谢!

2 个答案:

答案 0 :(得分:1)

您提到程序运行时存在的一个类。 Program可能是班级的好名字,但你可以想得更好。

---小一点,但我们会回到你的主线问题---

您还提到了有效范围是一个“会话”的变量。我会创建第二个类Session并重构,直到这些变量在该类中。然后在构造函数中初始化变量。

在开始移动之前,请输入测试用例(尽可能最好)以验证正确的变量行为。移动后,清理这些测试用例并使其健壮。测试你通常不会做的事情,或允许完成(如果你完全控制所有代码),比如传递空变量等等。

通过这样做,您可以有效地取消遗留一小部分代码。相信我,这是值得的。

---好的,不要回到我们经常安排的问题---

你是对的,这条路径设置了一个数据类;然而,数据是在正确的位置,现在只有逻辑在错误的位置。

查找访问群集中移动数据的代码块。如果直接访问数据,您可以通过使成员保密并在整个产品中查看编译失败或使用IDE功能来执行此操作。跟踪每次访问,寻找读写行为模式。

一旦确定了(比方说)读取行为的模式,就在“数据类”中创建一个虚拟方法,其名称接近您的想法。将该方法放在远程调用方中,然后将语句“移动”到方法中。实际上,这会将它们转移到仅用于数据的类中。

现在你有一个包含数据和一些行为的类。继续使用模式,直到您确信所有“正确”的行为都应该是它的位置。然后再次检查您的暴露“吸气剂和固定剂”方法是否确实需要公开曝光。

在整个过程中保持测试的最新状态,并验证它们是否也涵盖了新创建的方法。它们是你的安全网,你真的没有改变任何东西,除非你打算改变代码。

祝你好运。

---编辑回答问题---

I will definitely go through my code and have a look to see where that's appropriate - some of the reading I mentioned suggested that. However, I do feel like because the Workflow Class (i.e. "Program class") already contains little/simple logic, that moving more of it over to the WorkFlowVariables ("Session") class for the sake of avoiding a Data Class seems a bit silly? 

如果这是出于愚蠢的原因,这只是愚蠢的。你似乎认为移动代码的原因与避免气味有关。这根本不是移动代码的原因。移动代码的原因是因为它处于错误的位置。

如果我的逻辑与我的数据分开,那么我的数据必须在外部进行管理。也就是说,每当有人想要对我的数据执行某些操作时,由“某人”来执行此操作的方式不会使我的数据内部不一致。这可能包含在设置数据类以包含变量值之前检查变量是否为空,根据成员变量的组合更新公开值,在值更改时发送通知,标记“进一步处理”项,存储审计日志条目等等。

Won't it also tightly couple those classes? 

这在很大程度上取决于你在哪里进行拆分,以及你如何进行拆分。重构并不意味着你一次解决所有问题。这意味着您修改代码的方式不会更改其功能。在重构步骤之间,您最终将进行代码更改。如果没有先前的重构步骤,这些小代码更改是不可能的(或者至少很难),并且它们可能会引入错误(因为任何非重构更改都可以)。

哦,松散耦合在你为可扩展性设计的地方很好。没有任何东西可以扩展的松散耦合是浪费资源来编写你不需要的代码的典型例子。

疏松耦合的一个很好的例子:

  1. 一个工作流引擎,可以通过接口调用任务来执行未知数量的任务。
  2. 疏松耦合的一个坏例子:

    1. 当您需要强制执行步骤合同时,您决定让它们扩展一个步骤列表,以便您现在需要预先处理步骤列表以确保它们符合合同。例如,您使用无限可配置的“工作列表”类作为数据库更新例程的基础,但您的数据库更新框架现在需要验证每个“工作列表”是否打开和关闭数据库。
    2. 当然,您可以使用数据库工作列表验证器“修复”此问题。这意味着您可能需要一个数据库工作列表“工作管理器”,以确保在每个适当的时间点进行验证等。是的,您可以通过这种方式解决问题,但它将是脆弱的,代码将显示较差的数据位置

      在后一个例子中,使用“数据库工作列表”可以做得更好,该数据库工作列表使用可配置数量的中间步骤对所需的打开和关闭步骤进行硬编码。

答案 1 :(得分:1)

这看起来很好,实际上可能是State模式,因此该类的更好名称可能是SessionState

像国家一样思考它可以隐藏所有变量和条件逻辑。每个不同的州都会设置不同的变量。根据哪个更具意图揭示您的用例,从一个状态转换到下一个状态将设置变量或设置变量将转换状态(或两者)。