到目前为止,我希望你已经意识到面向对象的设计非常棒。尽管这不是编写软件的唯一方法。事实上,C#是一种多范式语言。了解其他范例有助于识别 OOP 的优势和劣势。
过程编程已经存在很长时间了。这个名字来自过程或子程序,也称为方法。过程语言,像面向对象语言一样,有方法(过程)、变量、数据结构,甚至某种模块化。然而,过程语言没有类的概念。如果两个方法需要访问同一个变量,则该变量必须在全局级别声明。在较大的应用程序中,这就成了问题,因为很容易不小心操作这样一个变量并搞乱整个应用程序。众所周知的过程语言有 C 语言、Pascal 语言、FORTRAN 语言和 BASIC 语言。
尽管函数式编程从 50 年代就已经出现,但最近它的受欢迎程度有所上升。1958 年,第一个函数式语言是 LISP。在函数式语言中,与数学函数密切相关的函数具有中心作用,就像在数学中,函数只能在给定的输入下工作一样。这使得对函数程序进行推理成为可能,因为您知道函数不会改变任何程序状态。因为函数非常重要,所以很容易从现有函数中构造新函数,从函数中返回函数,以及将函数作为输入传递给其他函数。我们说功能是一等公民。函数式语言通常有一个复杂的类型系统,这使得没有必要声明变量类型,但是类型安全是有保证的。
C#有一些函数构造。例如,LINQ 和 lambda 是从函数式语言中借来的。其他流行的函数式语言有 F#、ML、Erlang 和 Haskell。
函数式编程和面向对象编程的一大区别是状态的概念。每种面向对象语言都有状态,类内的变量决定了类的状态。拥有状态对一门语言的影响比你想象的要大。例如,考虑以下增量器类:
代码清单 105:增量器类
public class Incrementer
{
private int counter = 0;
public void Increment()
{
counter += 1;
}
public int GetIncrement()
{
return counter;
}
}
任何增量器实例的状态都由计数器的值决定。当我们调用增量函数时,我们不能知道实例的状态,除非我们在调用增量之前知道状态。这使得很难对程序进行推理。当我们调用 GetIncrement 时,我们不知道它会返回什么,因为我们不知道对象的状态。
让我们来看一个例子。假设我们有一个数学课,它有一个函数 Add。
代码清单 106:一个数学类
public class Math
{
public int Add (int a, int b)
{
return a + b;
}
}
在这种情况下,我们知道,如果我们调用参数为 1 和 2 的函数,结果将是 3。然而,这非常重要,在面向对象语言中,我们永远无法确定。
代码清单 107:添加副作用
public class Math
{
private int something;
public int Add (int a, int b)
{
something += 1;
return a + b;
}
}
没有什么能阻止我们编写一个加法函数来改变我们数学课的内部状态。一个类内部状态的改变被称为副作用。
函数式编程禁止这种副作用。这使得在给定输入参数的情况下预测函数的结果成为可能。虽然很难;尝试编写一个没有状态或副作用的应用程序。
OOP 在很多方面都很棒。它不太擅长的一件事是处理所谓的交叉问题。贯穿各领域的问题是整个应用程序中使用的一般责任。例如,考虑日志记录,您可能希望在每个层和每个项目中的任何方法中实现它。结果是你所有的方法都是从类似 Logger 的东西开始的。日志(当前方法名,输入参数)。另一个例子类似于. NET 中的 INotifyPropertyChanging 和 INotifyPropertyChanged 接口。它们可以在一个类上实现,然后每当属性改变其值时,该类就会引发 PropertyChanging 和 PropertyChanged 事件。实现相当繁琐:
代码清单 108: INotifyPropertyChanging 和 INotifyPropertyChanged
public class SomeClass : INotifyPropertyChanging, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public event PropertyChangingEventHandler PropertyChanging;
private int someProperty;
public int SomeProperty
{
get { return someProperty; }
set
{
if (value != someProperty)
{
RaisePropertyChanging("SomeProperty");
someProperty = value;
RaisePropertyChanged("SomeProperty");
}
}
}
private void RaisePropertyChanging(string propertyName)
{
if (PropertyChanging != null)
{
PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
}
}
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
那是一大堆样板代码!一个需要一行代码的类现在已经超过 30 个了!
面向方面编程旨在通过动态改变现有代码来解决这个问题。代理或装饰模式经常被用来实现这一点。最著名的 AOP 框架之一可能是 PostSharp【6】。另一个是城堡项目【7】的动态火箭。