用OO语言编写逻辑过程软件的最简洁方法


12

我是一名电气工程师,我不知道自己在做什么。请保存我的代码的未来维护者。

最近,我一直在研究一些较小的程序(在C#中),其功能在逻辑上是“过程的”。例如,其中之一是一个程序,该程序从不同的数据库收集信息,使用该信息生成某种摘要页面,将其打印出来,然后退出。

所有这些所需的逻辑约为2000行。我当然不想像以前的开发人员所做的那样将所有内容全部填充到一起main(),然后用来“清理” #region(颤抖)。

这是我已经尝试过但不太满意的一些事情:

为每个粗略的功能(例如DatabaseInfoGetter,SummaryPageGenerator和PrintUtility)创建静态实用程序。使主要功能看起来像:

int main()
{
    var thisThing = DatabaseInfoGetter.GetThis();
    var thatThing = DatabaseInfoGetter.GetThat();
    var summary = SummaryPageGenerator.GeneratePage(thisThing, thatThing);

    PrintUtility.Print(summary);
}

对于一个程序,我什至使用了接口

int main()
{
    /* pardon the psuedocode */

    List<Function> toDoList = new List<Function>();
    toDoList.Add(new DatabaseInfoGetter(serverUrl));
    toDoList.Add(new SummaryPageGenerator());
    toDoList.Add(new PrintUtility());

    foreach (Function f in toDoList)
        f.Do();
}

这一切都不对。随着代码的变长,这两种方法都开始变得丑陋。

构造这样的东西的好方法是什么?


2
我不确定这是否严格重复,但是请阅读具有许多参数的Single方法与必须按顺序调用的许多方法

1
可能引起
Dan Pichelman

@Snowman,作为刚开始编写较大代码段的人,可以放心地看到我不是唯一遇到此类问题的人。我喜欢你对这个问题的回答。它有助于。
伦巴第

@Lombard将复杂性降低到可管理水平的想法是一项非常好的开发技能。没有人能理解大型代码库,甚至超人(也许是蝙蝠侠)也无法理解。找到简单表达想法并自我记录代码的方法至关重要。

“最近,我正在研究一些较小的程序(在C#中),其功能在逻辑上是“过程上的”。例如,其中一个是从不同数据库收集信息并使用该信息生成某种摘要的程序。页,将其打印,然后退出。”:您确定不会将“过程性”与“命令性”混淆吗?过程和面向对象都是(可能)必须的,即它们可以表示要按顺序执行的操作。
乔治

Answers:


18

由于您不是专业程序员,因此我建议您坚持简单性。对于程序员来说,采用模块化的过程代码并在以后进行面向对象的设计要比修复一个编写得不好的OO程序要容易得多。如果您没有经验,则可以创建OO程序,这些程序可能会变成一团糟,既无济于事,也无济于事。

我认为您的第一个直觉是,第一个示例中的“此物即物”代码是正确的轨道。您想做什么很明显。不必太担心代码效率,清晰度更为重要。

如果代码段太长,请将其分成几小块,每个块都有自己的功能。如果太短,请考虑使用更少的模块,并增加更多的模块。

----后记:OO设计陷阱

成功地使用OO编程可能很棘手。甚至有些人认为整个模型存在缺陷。当我第一次学习面向对象的编程时,有一本非常好的书叫做《Thinking in Java》(现在是第4版)。同一作者有一本对应的C ++书。实际上,关于程序员处理面向对象编程中常见陷阱的另一个问题。

有些陷阱很复杂,但是有很多方法可以以非常基本的方式制造问题。例如,几年前,我公司有一名实习生,他写了我继承的某些软件的第一个版本,他为可能有一天有多种实现。当然,在98%的情况下,只有一个实现,因此代码加载了未使用的接口,这使调试变得非常烦人,因为您无法退回接口调用,因此最终不得不做一个文本搜索实现(尽管现在我使用的是IntelliJ,它具有“显示所有实现”功能,但在那时我还没有)。这里的规则与过程编程相同:始终对一件事进行硬编码。仅当您有两件或更多件东西时,才创建一个抽象。

可以在Java Swing API中找到类似的设计错误。他们为Swing菜单系统使用发布-订阅模型。这使Swing中的菜单创建和调试成为一场噩梦。具有讽刺意味的是,它是完全没有意义的。实际上,从来没有需要“订阅”多个功能的情况。此外,由于通常总是使用菜单系统,因此发布-订阅完全失败了。并不是说函数先订阅然后随机取消订阅。Sun的“专业”开发人员犯了这样的错误,这一事实表明,即使是专业人士,也很容易在OO设计中制造巨大的螺丝钉。

我是一个非常专业的开发人员,在OO编程方面拥有数十年的经验,但是即使我将是第一个承认我不知道的东西,甚至现在我对使用很多OO还是非常谨慎。我曾经听过一位来自OO狂热者的同事的漫长演讲,内容涉及如何进行特定的设计。他确实知道他在做什么,但是老实说,我很难理解他的程序,因为它们具有如此复杂的设计模型。


1
+1代表“清晰度比[效率]重要得多”
Stephen

您能否指向一个描述“糟糕的OO程序”的内容的链接,或者在编辑中添加有关此内容的更多信息?
伦巴第

@Lombard我做到了。
泰勒·德登

1

第一种方法很好。在像C这样的过程语言中,标准方法是将功能划分为多个名称空间-使用静态类之类DatabaseInfoGetter基本上是同一回事。显然,与将所有内容都分解到一个类中相比,即使将所有内容分解为方法,这种方法也导致了更简单,更具模块化和可读性/可维护性的代码。

说到这,请尝试将方法限制为执行尽可能少的动作。一些程序员更喜欢粒度一些,但大型方法始终被认为是有害的。

如果您仍在复杂性上挣扎,则很可能需要进一步分解程序。在层次结构中创建更多级别-可能DatabaseInfoGetter需要引用一些其他类,诸如此类ProductTableEntry。您正在编写过程代码,但请记住,您正在使用C#,而OOP为您提供了一系列降低复杂性的工具,例如:

int main() 
{
    var thisthat = Database.GetThisThat();
}

public class ThisThatClass
{
    public String This;
    public String That;
}

public static class Database 
{
    public ThisThatClass GetThisThat()
    {
        return new ThisThatClass 
        {
            This = GetThis(),
            That = GetThat()
        };
    }

    public static String GetThis() 
    { 
        //stuff
    }
    public static String GetThat() 
    { 
        //stuff
    }

}

另外,请注意静态类。只要您通常只有一个数据库,数据库类就是不错的选择。

最终,如果您考虑数学函数而C#不适合您的风格,请尝试诸如Scala,Haskell等之类的东西。它们也很酷。


0

我要晚4年才能做出答复,但是当您尝试使用OO语言进行程序化操作时,您正在研究反模式。那不是你应该做的!我发现了一个博客,致力于解决这种反模式并提供了解决方案,但是它需要大量的重构和思维方式的改变。有关更多详细信息,请参见面向对象代码中的过程代码
正如您提到“ this”和“ that”一样,您基本上是在建议您拥有两个不同的类。一个此类(坏名字!)和一个那个类。您可以例如翻译为:

var summary = SummaryPageGenerator.GeneratePage(thisThing, thatThing);

到这个:

var summary = SummaryPageGenerator.GeneratePage(new This(DatabaseInfoGetter), new That(DatabaseInfoGetter));

现在,很有趣的一点是,要查看这2,000行以上的过程代码,并确定如何将各个部分组合到不同的类中,并将各种过程向下移动到这些类中。
每节课都应该简单,只专注于做一件事。在我看来,代码的某些部分已经在处理超类,而实际上它们太大了而无法使用。例如,DatabaseInfoGetter听起来令人困惑。反正得到什么?听起来太普通了。但是SummaryPageGenerator非常具体!并且PrintUtility再次变得通用。
因此,一开始,您应该避免使用类的通用名称,而应改用更具体的名称。例如,PrintUtility类将是一个带有Header类,Footer类,TextBlock类,Image类的Print类,并可能以某种方式指示它们在打印本身中的流动方式。不过,PrintUtility 命名空间
对于数据库,类似的故事。即使将实体框架用于数据库访问,也应将其限制为使用的对象,并将其划分为逻辑组。
但我想知道四年后您是否仍在处理此问题。如果是这样,那么就需要从“过程概念”转变为“面向对象的概念”。如果您没有经验,这将是一个挑战...


-3

我认为,您应该更改代码,使其简单,一致且可读。

我写了一些有关此主题的博客文章,并相信我它们很容易理解: 什么是好的代码

简单:就像电路板包含不同的部分一样,每个部分都有责任。代码应分为较小,较简单的部分。

一致:使用样式,这是命名元素和事物的标准。您喜欢一直以相同方式工作的工具,并且想知道在哪里可以找到它们。代码也一样。

易读:除非使用首字母缩略词,否则除非该首字母缩略词在该软件所针对的领域内得到广泛使用并广为人知(mattnz),否则请使用清晰易懂的可发音名称。


1
如果首字母缩略词在该软件所针对的领域中得到广泛使用和已知,则可以接受。尝试编写空中交通管制软件而不使用-我们有首字母缩略词由首字母缩略词组成-没有人会知道您在说什么是您使用的全名。诸如HTTP用于网络,汽车中的ABS之类的东西是完全可以接受的。
mattnz
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.