关于面向对象编程(OOP)哲学的建议

3
我正在尝试用C#启动一个大型项目。我之前使用的是Delphi,其中默认情况下每个表单都在应用程序启动时创建,并且表单引用被保存在(哇)全局变量中。因此,我正在尝试将自己的思维适应为100%面向对象的环境,我的头有点晕。
我的应用程序将拥有大量的类。这些类中大多数只需要一个实例。因此,我想到了静态类。但我不太确定为什么,因为我在这里阅读的大部分内容都说,如果我的类将保持状态,这意味着任何属性值,我应该使用单例结构。好吧。但是有人认为单例也是邪恶的,原因让我无法理解。
这些类中没有一个会在除了此程序之外的地方使用。因此,它们可以作为常规对象(而不是单例或静态类)正常工作。
然后还有对象之间的交互问题。我想创建一个全局类,其中包含对许多这些类的单个实例的公共静态属性的引用。我也考虑过将它们作为MainForm的属性(静态或实例,不确定)进行设置。然后我将使我的每个类都知道MainForm作为Owner。然后各种对象可以相互引用,例如Owner.Object1,Owner.Object2等。
我担心我的电子墨水用完了,或者至少耐心已经被耗尽。我希望我已经清楚地解释了我的彻底困惑状态。我只是寻求有关我的情况下最佳实践的建议。所有输入都受到欢迎和赞赏。
提前谢谢, David Jennings

您可以从Delphi表单中删除全局变量,并根据需要实例化和释放它们。 - TrueWill
1
感谢所有抽出时间回复我的人。你们给了我很多思考的东西。这是一个伟大的社区。我必须承认,当我说大多数课程只需要一个实例时,我夸张了。事实上,只有大约12个课程中的3个需要一个实例。再次感谢鼓励我退后一步,仔细看看我的一些类决策。在我逐渐提高水平的过程中,这个网站将成为我最重要的C#智慧来源之一。 - David Jenings
6个回答

4

从过程式或其他非面向对象编程语言转换到面向对象编程时,认为“每个XYZ一个巨大的静态类”是很正常的,但这通常意味着您没有真正考虑所代表的对象,因为一个类应该表示要操作的对象。

因此,您需要退后一步,查看数据及其所代表的内容。您可能会说,“嗯,这是数据!它代表数字!”,但您必须抽象出数据所代表的内容。数据所代表的“东西”就是对象。然后,您对所代表的“东西”执行的操作就成为您的方法。


这个项目实际上是我大约8年前用Delphi开发的系统的重写。自那以后,我一直在维护和改进应用程序,现在我决定是时候放弃Delphi了。Delphi是一种面向对象的语言(大多数情况下)。它只是混合了全局存储部分(大多数面向对象程序员无疑会觉得令人不安)。因此,就数据/行为转换为类而言,我认为我已经做得很好了。我有点迷茫的地方是,找出离开全局空间的最佳方法。 - David Jenings

2
你可以尝试一下并看看效果如何。考虑使用NUnit进行开发;如果你这样做,你会发现你的代码足够灵活,使得进行架构更改不会太困难。
我更喜欢避免使用静态类和静态数据,但有时为了近期问题而使用它们是有意义的。对此,我的建议是考虑实际拥有各种表单的对象。通常情况下,人们似乎想把主要表单作为“主应用程序事物”,但也许有更好的所有者适合该表单。
总之,除了谈论和思考可能性之外,我建议继续尝试一些选项并看看它们的效果。写完软件后不要害怕更改它!

谢谢你的回复。这正是我开始时的态度。我相信无论我如何解决这些问题,我都能让应用程序正常工作。我只是想在这个过程中学到一些东西,并且避免应用程序一开始就变得混乱不堪。 :) - David Jenings

2
静态类和单例模式具有相同的缺点:
  • 与许多其他类紧密耦合
  • 隐藏依赖项(很难知道其他类使用了什么)
  • 测试困难
  • 本质上是全局数据
  • 对于单例模式,承担多个职责(无论它们做什么+生命周期管理)
在许多情况下(不是所有情况),解决方案是基于依赖反转原则(Dependency Inversion PrincipleSOLID 原则之一,@CodeToGlory提到过)的依赖注入(DI)。这可以手动完成或使用DI容器框架
Mark Seemann有一本关于.NET中的依赖注入的优秀书籍;我强烈推荐。

1

单例通常是设计中出现的许多问题的根源。然而,我认为创建单例和全局变量作为在应用程序中传递它们的手段是一种代码异味。

我觉得最好先了解SOLID原则。此外,还有几个关于这个主题的dimecasts,你应该观看并理解。

另一本实用的书籍是Kent Beck的《实现模式》。理解面向对象编程是一回事,将其应用于现实世界是另一回事。

看看Windows Presentation Foundation,这是一个用于构建Windows客户端应用程序的下一代演示系统。

我想我已经向你提供了足够的信息,但要轻松一点,一步一步地走。我认为你应该跟随这里的几个轨道,以达到你想要的目标。微软提供了一些原型,你可以从中学习,然后与面向对象编程进行创新。


0

在阅读您的解释时,我觉得您可能仍然在以Delphi的范式思考,如果您认为有那么多类应该是静态或单例的。我不知道我是否曾经遇到过只有一个对象的项目。因此,我的倾向是您的对象分解可能存在缺陷。

实际上,您应该开始思考您在程序中所代表的内容。问问自己,如果您有很多只有一个实例的对象,为什么只有一个实例?这是因为您以奇怪的方式保存数据,例如具有匹配索引的多个数组。

话虽如此,C#确实具有一些良好的内置单例语义,因为您不必在实例方法中进行空值检查。

public class SingletonClass{
     private static SingletonClass _instance;

     private SingletonClass(){}

     public static Instance
     {
         get
         {
             if(_instance == null){
                _instance = new SingletonClass();
             }
         }
     }

}

在C#中可以这样写:

public class SingletonClass{
     private static SingletonClass _instance = new SingletonClass();

     private SingletonClass(){}

     public static Instance
     {
         get
         {
            return _instance;
         }
     }

}

如果你坚持使用这种设计模式,我会建议倾向于使用单例模式。 单例模式,像其他东西一样,有时机和场合,我不会编写大部分对象都是单例的整个应用程序。

我强烈鼓励您思考为什么要使用具有静态方法或单例的全局状态。


0

以下是一些关于编程的随机想法,可能有用,也可能没用:

  • 从数据开始而不是程序结构。分析数据并找出你要概念化表示的内容,然后将其对象化。

  • 我的经验是,在过程式代码中,你会思考“我现在在做什么?”但在面向对象的代码中,你会思考“我将来可能想做什么?”

  • 至少会有一个主对象,即使这个对象本身就是应用程序对象。不要在这个对象中放太多东西,尽管这很诱人。越多地在概念上相关的对象中表示数据、状态和行为,新开发人员学习和维护应用程序就会更容易。

  • 做一个原型。建立一个你知道会丢弃的缩小版应用程序,当你到达某个点时,回顾你的代码,决定哪些对象运作良好,哪些不行,然后重新开始。

关于单例设计模式,没有恶意挑衅的意思,无需理会那些反对者。有时候,单例设计模式非常有用,就像EAV数据模型有时候很完美,而MVC则是世界上最糟糕的东西一样。如果你需要拧螺丝,就要使用螺丝刀。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接