这篇文章的目的是比较面向对象的编程和方例化编程之间的一些差异,同时使用这两种方法建模相同的功能。自20世纪70年代以来,面向对象的编程已经存在,而面向协议的编程在过去几年中只有流行,即使协议/接口已经存在一段时间。这篇文章的目标不是在架构之间挑选一个胜利者,而是试图帮助您选择最佳解决方案,了解您正在努力的项目类型。

面向对象的编程

Object-oriented programming is a paradigm based on the concept of an “object”, which in most programming languages is defined by a data structure (e.g. Class) that contains information about attributes in the form of properties, and the actions performed by or to an object in the form of methods. This paradigm allows us to encapsulate the properties and actions of an object into a single type, the entity we are trying to represent in our code.

让我们创建一个基本的类图,展示我们如何为面向对象设计设计动物类层次结构。

该图显示我们有一个名为的超级类动物并命名的三个子类这将继承超类的所有内部和公共属性和方法。

让我们假设我们正在开发一个Tamagotchi.我们可以使用以下要求创建动物的游戏类型:

  • 我们有三种不同的动物类别(陆地,空气和水)。
  • 动物可以是多个类别的成员。

让我们开始对面向对象的设计:

  • 创造一个Anicalenvironment.将用于识别动物可以存活的地方。
  • 创造一个动物超类,它将包含所有动物的属性。前三个属性是一系列Anicalenvironment.,动物的名称和年龄。另外三个属性将包含动物可能的每个环境的速度。
  • 创造方法,前三种方法将用于使我们的动物锻炼和烧一些卡路里,另外三种方法让我们知道动物可以做些什么。
Enum Animalenvironment {案例陆壳空气盒水}级动物{让环境:[AnellenEnvironment]让名称:字符串让exce:String让Landspeed:int Let airspeed:int让waterspeed:int init(环境:[Animalenvironment],名称:字符串,年龄:string,landspeed:int,airspeed:int,waterspeed:int){self.environments =环境self.name = name self.age =年龄self.landspeed = landspeed self.airspeed = airspeed self.waterspeed = waterspeed} funcrun(){} func fly(){} func swim(){} func canrun() - > bool {环境.Contains(.land)} func canfly() - > bool {环境.Contains(.air)} Funccanswim() - > bool {环境.Contains(.water)}}

现在,让我们来看看我们如何归属动物通过创建类,和如下图所定义的类。

最终类CAT:Animal {init(名称:字符串,年龄:String,Landspeed:int){super.init(环境:[.land],名称:名称:姓名:年龄,年龄,土地速度:Landspeed,Airspeed:0,Waterspeed:0)} override func运行(){print(“运行”)}} Final Class Bird:Animal {init(名称:字符串,年龄:String,Landspeed:Int,Airspeed:int){super.init(环境:[。Air,.land],名称:名称,年龄:年龄,Landspeed:Landspeed,Airspeed:Airspeed,Waterspeed:0)}覆盖Func Run(){Print(“Run”)}覆盖Func Fly(){PRINT(“Fly“)}}}}}}}}}}}}} FISH:动物{init(名称:字符串,年龄:string,waterspeed:int){super.init(环境:[.water],名称:名称,年龄:年龄:年龄,Landspeed:0,Airspeed:0,waterspeed:waterspeed)}覆盖func swim(){print(“swim”)}}

猫,鸟和鱼课程是子类的动物类,我们通过为每个初始化器创建初始化来开始这些类。为了例如,子类,我们指定了动物环境是。土地,空速沃特索斯飞行由于猫无法在这些环境中存活。我们还覆盖属于每只动物的功能并添加其实现。

如您所见,即使不是所有子类都需要所有超类信息,超类的所有信息都会暴露于子类。它很容易图片一个场景,其中更多代码正在添加到超类中,并且更难以维护。即使对于最经验丰富的开发人员而言,它也很容易在阵列或速度属性中输入错误的值,导致意外行为。

福利

  • 通过继承的代码可重用性。
  • 多态性灵活性。
  • 封装。

缺点

  • Swift是一种单一的继承语言,我们只能为动物类拥有一个超级类,超类需要包含三个类别所需的代码。
  • 遗传类型不需要的功能。它可以导致臃肿的超类,因为我们可能需要包括只有少数子类所需的功能。
  • 我们无法在我们的超类中创建常量,这些超类可以由子类设置。
  • 在SWIFT中,值类型不能使用继承,仅是引用类型。
  • 打破的机会LSP.(LISKOV替代原则)如果不是很好的话。

协议方向编程

正如我们对面向对象的设计所做的那样,让我们​​创建一个图表,该图显示了如何以方向的方式设计动物类型。

正如我们所看到的,面向协议的设计侧重于实现关注的协议,这些协议涉及除了在目标设计中设计的细节之外的要求。这是一个很好的例子ISP.(界面隔离原则),客户端不需要知道他们不使用的属性和方法,以及(依赖反转原理)设计依赖于抽象而不是结识。

让我们开始通过以下方式进行协议的设计:

  • 创造一个动物包含其他协议可以利用的共享要求的协议。
  • 创造A.Landanimal,Airanimal和Wateranimal协议,所有人都会从中继承动物协议并为每个协议添加速度属性。
协议动物{var名称:string {get} var年龄:string {get}}协议landanimal:动物{var landspeed:int {get} func run()}协议airanimal:动物{var airspeed:int {get} func fly(Partrationimal:Animal {var waterspeed:int {get} func swim()}。

该图显示了与协议一起使用的两种技术:

协议继承:当协议可以从一个或多个协议继承这些要求时。这类似于面向对象编程中的类继承,而不是继承功能,而是继承要求。

协议组成:它允许类型符合多个协议,我们可以看到发生这种情况结构体。这也可以在面向对象的编程中可以实现,其中类可以具有单个或无继承,但符合多个协议。

值得注意的是,任何符合的类型动物协议,或符合从继承的协议的任何类型动物协议,将自动访问属性和方法。

现在,让我们来看看我们如何实施,和我们图中定义的结构。正如我们所看到的,所面向方案的设计只暴露了每只动物的必要要求,这是一个令人担忧的分离的一个很好的例子。此外,无需创建自定义初始化程序,因为我们正在使用a塑造代替

struct cat:landanimal {let name:string让exce:string let landspeed:int func运行(){print(“运行”)}} struct bird:landanimal,airanimal {let name {let name {let name {let name {let nameairspeed:int func run(){print(“run”)} func fly(){print(“fly”)}} struct fish:wateranimal {let name:string让exce:string让waterspeed:int func sweic(){打印(“SWIM”)}}

我们还实现了缺少的方法能跑可以飞会游泳正如我们在面向对象的设计中,但这一次让我们延伸动物提供给我们提供为所有符合类型提供公共实现的方法的方法。

延伸动物{func canrun() - > bool {self是landanimal} func canfly() - > bool {self是aireanimal} func canswim() - > bool {self是wateranimal}}

请注意,在SWIFT中,我们使用关键字检查实例是否为特定类型和关键字将实例视为特定类型。正如你所看到的,我们的结构可以访问所有方法。

让猫=猫(名字:“喵喵”,年龄:“4”,landspeed:12)让坎伦伦:bool = cat.canrun()ver fly:bool = cat.canfly()让canswim:bool = cat.canswim()

面向对象的和协议的设计都为我们提供了使用多态性的能力,这是多种类型的单个接口。让我们看看它在两个设计中的样子。

让猫=猫(姓名:“喵喵”,年龄:“4”,Landspeed:12)让鸟=鸟(名称:“Tiki”,年龄:“2”,Landspeed:3,Airspeed:20)让鱼=鱼(名称:“泡沫”,年龄:“1”,Waterspeed:8)让动物:[动物] = [猫,鸟,鱼]

正如您所看到的,对于两种方法,使用多态性看起来完全相同,而在面向对象的设计中动物是一个超级类和面向方案的设计动物是一个协议。

福利

  • 面向协议的设计允许我们使用类,结构和枚举。
  • 我们可以使用协议扩展以将功能添加到符合我们协议的类型。
  • 能够将任何属性定义为常量。
  • 协议组成允许数据结构实现多种要求。
  • 与泛型相结合时,协议变得非常强大。
  • 清洁代码。
  • 更容易找到错误。

缺点

  • 滥用协议继承和协议扩展可能导致复杂的系统。

结论

正如您所看到的,我们能够使用acrigigms(面向对象的编程和面向协议的编程)来实现相同的目标。在一天结束时,正确地设计您的体系结构是将给您一个稳固稳定的系统,独立于您选择使用的方法。尝试收集您将在纸张上工作的项目的所有要求,在纸上设计它,然后思考范例将适合您更好。