这篇文章的目的是比较面向对象的编程和方例化编程之间的一些差异,同时使用这两种方法建模相同的功能。自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.

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

这个图显示了一个名为动物以及三个子类这将继承超类的所有内部和公共属性和方法。

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

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

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

  • 创造一个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(name:String,age:String,landSpeed:Int){超级初始化(environments:[.land],name:name,age:age,landSpeed:landSpeed,airSpeed:0,waterSpeed:0)}覆盖func run(){print(“run”)}最终类Bird:Animal{init(name:String,age:String,landSpeed:Int,airSpeed:Int){超级初始化(环境:[空气,.land],name:name,age:age,landSpeed:landSpeed,airSpeed:airSpeed,waterSpeed:0)}覆盖func run(){print(“run”)}覆盖func fly(){print(“fly”)}最终类Fish:Animal{init(name:String,age:String,waterSpeed:Int){超级初始化(环境:[水],名称:名称,年龄:年龄,陆地速度:0,空速:0,waterSpeed:waterSpeed)}重写func swim(){print(“swim”)}

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

如您所见,来自超类的所有信息都暴露给子类,尽管并非所有子类都需要所有的超类信息。很容易想象这样一个场景:更多的代码开始添加到超类中,并且维护起来更加困难。即使是最有经验的开发人员,也很容易在数组中输入错误的值或加速属性,从而导致意外的行为。

好处

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

缺点

  • Swift是一种单一的继承语言,我们只能为动物类拥有一个超级类,超类需要包含三个类别所需的代码。
  • 继承类型不需要的功能。它可能导致超类膨胀,因为我们可能需要包含只有少数子类需要的功能。
  • 我们不能在我们的超类中创建可以由子类设置的常量。
  • 在SWIFT中,值类型不能使用继承,仅是引用类型。
  • 打破的机会LSP公司(李斯科夫替代原理)如果设计得不好。

协议方向编程

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

如我们所见,面向协议的设计侧重于实现一个协议,该协议关注的是需求,而不是面向对象设计中设计的细节。这是一个很好的例子ISP.(接口隔离原则),其中客户机不需要知道他们不使用的属性和方法,以及(依赖倒置原则)设计依赖抽象而不是具体化。

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

  • 创造一个动物包含其他协议可以利用的共享要求的协议。
  • 创造A.陆地动物、空中动物和水生动物协议,所有人都会从中继承动物协议并为每个协议添加速度属性。
协议动物{var name:String{get}var age:String{get}}协议陆地动物:动物{var landSpeed:Int{get}func run()}协议空中动物:动物{var空速:Int{get}func fly()}协议水动物:动物{var水速:Int{get}func swim()}

该图向我们展示了用于协议的两种技术:

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

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

需要注意的是,任何符合动物协议,或符合从动物协议,将自动具有访问属性和方法的权限。

现在,让我们来看看我们如何实施,和在我们的图表中定义的结构。正如我们所看到的,面向协议的设计只暴露了每个动物的必要需求,这是分离关注点的一个很好的例子。另外,不需要创建自定义初始值设定项,因为这里我们使用的是塑造而不是班级

struct Cat:LandAnimal{let name:String let age:String let landSpeed:Int func run(){print(“run”)}struct Bird:LandAnimal,AirAnimal{let name:String let age:String let landSpeed:Int let airSpeed:Int func run(){print(“run”)}func fly(){print(“fly”)}}}struct Fish:WaterAnimal{let name:String let age:String let waterSpeed:Int func swim(){print(“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”,陆地速度:12)让鸟=鸟(名字:“蒂基”,年龄:“2”,陆地速度:3,空速:20)让鱼=鱼(名字:“泡泡”,年龄:“1”,水速:8)让动物:[动物]=[猫,鸟,鱼]

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

好处

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

缺点

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

结论

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