>最近有人看了我的代码,并评论说它太程序化了。需要明确的是,他们看到的代码并不多 - 只是一个部分,清楚地概述了应用程序中采取的逻辑步骤。
if(downloadFeeds(ftpServer, ftpUsername, ftpPassword, getFtpPathToLocalPathMap())) {
loadDataSources();
initEngine();
loadLiveData();
processX();
copyIds();
addX();
processY();
copyIds();
addY();
pauseY();
resumeY();
setParameters();
}
然后,这些不同的方法创建一大堆不同的对象,并根据需要对这些对象调用各种方法。
我的问题是 - 一段代码清楚地驱动您的应用程序,例如这个,是否表明过程编程,如果是这样,还有什么更 OO 的方式来实现相同的结果?
非常感谢所有评论!
好吧,这段代码所在的类显然有太多的责任。我不会把所有这些东西隐藏在一个门面上,但不是把所有与一些ftp引擎、数据源和其他实体相关的东西都放在一个上帝对象(反模式)中,应该有一个业务流程,它有所有这些类型的实体。
所以代码可能看起来更像这样:
if(downloadFeeds(ftpServer, ftpUsername, ftpPassword, getFtpPathToLocalPathMap())) {
datasource.load();
engine.init();
data.load();
engine.processX(data);
data.copyIds()
foo.addX();
engine.processY();
// ...
}
数据源,引擎和所有其他组件都可以注入到您的业务流程中,因此a)测试变得更容易,b)交换实现得到简化,c)代码重用是可能的。
请注意,一段看起来过程化的代码并不总是坏的:
class Person {
public void getMilk()
{
go(kitchen);
Glass glass = cupboard.getGlass();
fridge.open();
Milk milk = fridge.getMilk();
use(glass, milk);
drink(glass);
}
// More person-ish stuff
}
虽然该代码显然看起来是程序性的,但可能没问题。它完全清楚这里发生了什么,不需要任何文档(Martin 的干净代码鼓励这样的代码)。只需牢记单一责任原则和所有其他基本的 OOP 规则即可。
哇!它似乎不像 OO 风格。像这样怎么样:
ConnectionData cData = new ConnectionData(ftpServer, ftpUsername, ftpPassword, getFtpPathToLocalPathMap());
if(downloadFeeds(cData)) {
MyJobFacade fc = new MyJobFacade();
fc.doYourJob();
}
我的作业外观.java
public class MyJobFacade {
public void doYourJob() {
/* all your do operations maybe on different objects */
}
}
顺便说一下立面图案http://en.wikipedia.org/wiki/Facade_pattern
我的问题是 - 是一段代码清楚地驱动你的应用程序,比如这个,指示过程编程,
不可能说这个代码片段是否"过于程序化"。
-
这些调用都可以是对当前对象上的实例方法、对单独对象或当前实例的实例变量进行操作。 这些至少在某种程度上会使代码成为 OO。
-
如果这些方法是
static
的,那么代码确实是过程性的。 这是否是一件坏事取决于方法是否访问和更新存储在static
字段中的状态。 (从名字来看,他们可能需要这样做。
如果是这样,还有什么更OO的方式来实现相同的结果?
如果不看代码的其余部分,很难说,但图片中似乎有一些隐含的对象;例如
- 数据源和(可能)数据源管理器或注册表
- 某种
engine
- 保存实时数据的东西,X 和 Y
- 等等。
其中一些可能需要是类(如果它们还不是类)......但是哪些取决于他们在做什么,它们有多复杂等等。 作为类没有意义的状态(无论出于何种原因)可以通过将静态变量转换为实例变量来使"更多 OO"。
其他答案建议特定的重构,摆脱所有全局变量,使用依赖注入等。 我的看法是,没有足够的信息来判断这些建议是否会有所帮助。
但这值得吗?
仅仅使应用程序"更多 OO"不是一个有用或有价值的目标。 你的目标应该是使代码更具可读性、可维护性、可测试性、可重用性等。 使用OO是否会改善问题取决于代码的当前状态,以及新设计和重构工作的质量。 简单地采用OO实践不会纠正糟糕的设计,也不会将"坏"代码变成"好"代码。
采取不同的方法来批评这一点,而不是它是否"过于程序化"。我希望你觉得它有点用。
首先,我没有看到任何函数参数或返回值。这意味着您可能正在使用各种全局数据,出于许多充分的理由,您应该避免使用这些数据,如果您愿意,可以在此处阅读:全局变量不好吗?
其次,我没有看到任何错误检查逻辑。假设 resumeY 失败并出现异常,也许问题出在 resumeY 中,但它也可能在 pauseY 中更高,或者远在 loadDataSources 中,并且问题仅在以后表现为异常。
我不知道这是否是生产代码,但它是分几个阶段重构的良好候选者。在第一阶段,您可以完成并使每个函数返回布尔成功与否,并在每个函数的主体中检查已知的错误情况。在你有一些错误检查之后,开始通过传入函数参数并返回结果数据来摆脱你的全局数据;您可以使您的函数在失败时返回 null 值或转换为异常处理,我建议异常。之后,请考虑使各个部分可单独测试;例如,您可以独立于数据处理功能测试下载源,反之亦然。
如果你通过并进行一些重构,你将开始看到可以模块化代码并改进它的明显地方。IMO 你应该少担心你是否足够 OOP,而更多地担心你是否可以 1.有效地调试它, 2.有效测试它和 3.在离开它并在 6 个月后回来维护它后理解它。
这个回复结束了很长,我希望你发现它的一部分有用。
是的。 您的方法不返回任何值,因此从代码段来看,它们正在对全局变量进行操作。 它看起来像教科书式的过程编程。
在更OO的方法中,我希望看到这样的东西:
if(downloadFeeds(ftpServer, ftpUsername, ftpPassword, getFtpPathToLocalPathMap()))
{
MyDatasources ds = loadDataSources();
Engine eng = initEngine(ds);
DataObj data = loadLiveData(eng);
Id[] xIds = processX(data.getX());
Id[] newXIds = xIds.clone();
data.addX(newXIds);
Id[] yIds = processY(data.getY());
Id[] newYIds = yIds.clone();
data.addY(newYIds);
pauseY();
resumeY();
someObject.setParameters(data);
}
保罗
以下是我学会区分的方法:
你的代码变得"程序化"的一个标志是当一个类有多个职责时(请参阅此链接关于单一责任原则)。 看起来所有这些方法都是由一个对象调用的,这意味着一个类正在管理一堆职责。
更好的方法是将这些职责分配给可以最好地处理它们的实际对象。 一旦这些职责被正确委派,就实现一个可以有效地驱动这些功能的软件模式(例如外观模式)。
这确实是过程式编程。如果你想让它更 OO,我会尝试这些组合:
- 尝试使用表示您持有的数据的类。例如,有一个 FeedReader 类来处理源,一个 DataLoader 类来加载数据,等等。
- 尝试将方法分离到具有内聚功能的类中。例如,将 resume()、pause() 分组到以前的 FeedReader 类中。
- 将 process() 方法分组到 ProcessManager 类中。
基本上,试着把你的计划想象成有一群为你工作的员工,你需要给他们分配职责。(PM 为我执行此操作,然后 DM 根据其结果执行此操作)。如果你把所有的责任都交给一个员工,他或她就会筋疲力尽。
我同意它看起来太程序化了。使它看起来不那么过程化的一个明显方法是让所有这些方法都成为类的一部分。Erhan的帖子应该让你更好地了解如何分解它:-)