
注解:
- 通过添加“标志”在类中实现新功能的示例。
- 效果。
- 替代方法和结果比较。
- 如何避免这种情况:“建筑过度杀伤”?
- 是时候改变一切了。
在开始之前,请注意以下几点:
- 这是关于软件体系结构的故事-从Bob叔叔使用的意义上讲。是的,那个。
- 本文中的所有字符,其名称和代码都是虚构的,与现实的任何巧合都是随机的。
假设我是一个项目的普通程序员。该项目是一款游戏,其中一个英雄(又名英雄)从左到右沿着一条完美的水平线走。怪物干扰了这个美好的旅程。在用户的命令下,英雄以用剑将它们切成卷心菜而著名,并且不会吹入胡须。该项目已经有10万行,“您需要更多行的功能!” 让我们看看我们的英雄:class Hero {
func strike() {
}
}
假设我的私人团队领导瓦西里(Vasily)也可以去度假。突然。谁曾想到?此外,情况按标准发展:一只猫头鹰飞来飞去,并迫切需要昨天,以便在游戏开始之前进行选择:用棍棒或剑为英雄出战。它必须非常快。是的,当然,他本人是一名程序员,工作了超过我所能行走的时间,所以他知道任务只有五分钟。但是就这样,估计需要2个小时。您只需要添加一个复选框和if-chik。注意:if-chik的值@!^%$#@ ^%&@!11 $其#$%@ ^%&!@ !!! 在&!^%#$ ^%!1 if-chic!我的想法是:“哈!两个小时!完成!”:class Hero {
enum WeaponTypes {
case sword:
case club:
}
var weaponType: WeaponTypes?
func strike() {
guard let weaponType = weaponType else {
assertionFailure()
return
}
switch (weaponType) {
case .sword:
case .club:
}
}
}
如果您在我的决定中找到了,那么,las:我有两个新闻要告诉您:- 好像不错:我们俩都能兑现。我们交付-从创造价值一词或从有趣(通过眼泪)代码一词。
- 坏的一面:没有瓦西里,kapets项目。
所以发生了什么事?到目前为止似乎什么都没有。但是,让我们看看接下来会发生什么(在我们竭尽所能使Vasily休假的同时)。然后是这样:质量检查部门将关注我们英雄的体重。不,这不是因为英雄必须节食,而是以下原因:var weight: Stone {
return meatbagWeight + pantsWeight + helmetWeight + swordWeight
}
我忘了修正重量计算。好吧,你认为,错误,与谁不发生?!一巴掌,他妈的tibidoch,准备好了:var weight: Stone {
let weightWithoutWeapon = meatbagWeight + pantsWeight + helmetWeight
switch (weaponType) {
case .sword: return weightWithoutWeapon + swordWeight
case .club: return weightWithoutWeapon + clubWeight
}
}
但是很快就解决了!考虑到重量,英雄现在正在跳跃,但是。以结果为导向的工作!这不是让您看肚脐,建筑宇航员的。好吧,真的,然后他纠正了一些。在拼写列表中,我必须这样做:var spells: [Spells] {
switch (weaponType) {
case .sword:
return spellsWithoutWeapon + swordSpells
case .club:
return spellsWithoutWeapon + clubSpells
}
}
然后Petya来了,打破了一切。好吧,事实是,我们对这个项目有这样的机会。不专心。他只需要在公式中增加“武器等级”的概念即可计算打击强度和英雄的体重。Petya错过了四个案例之一。但是什么都没有!我纠正了所有问题:func strike() {
switch (weaponType) {
case .sword:
case .club:
}
}
var weight: Stone {
let weightWithoutWeapon = meatbagWeight + pantsWeight + helmetWeight
switch (weaponType) {
case .sword: return weightWithoutWeapon + swordWeight / grade
case .club: return weightWithoutWeapon + pow(clubWeight, 1 / grade)
}
}
var spells: [Spells] {
}
不用说,什么时候(突然!)有必要添加弓形/更新公式,但又有被遗忘的情况,错误,仅此而已。什么地方出了错?我在哪里错了?瓦西里(Vasily)休假归国时,他说了什么(除了队友)?在这里,您无法进一步阅读该故事,例如,思考永恒的事物,关于建筑的思想。与那些仍然决定阅读的人一起,我们继续。因此,让我们看一下经典作品:过早的优化是万恶之源!
而且...呃...不是这样。事情就是这样:在OOP语言(例如Swift)中,有三种扩展类功能的主要方法:- 第一种方法是“天真”。我们刚看到他。添加一个复选框。增加责任。膨胀类。
- 第二种方法是继承。每个人都知道重用代码的强大机制。例如,可以这样:
从英雄(持有一把剑,但现在没有反映在英雄类的名称中)的杜比娜手中的弓和英雄那里继承新的英雄。然后在继承人中,覆盖更改的方法。这条路非常糟糕(请相信我)。- 使基类(或协议)成为英雄,并删除与特定类型的武器相关的所有功能作为继承人
:带
剑的英雄:英雄,带有弓箭的英雄:英雄,带有
杜比纳的英雄:英雄。
这样做更好,但是这些类的名称本身以某种方式使我们感到不悦,激烈,同时又感到悲伤和困惑。如果他们不看这样的人,那么我将尝试写另一篇文章,除了无聊的男性英雄之外,他们还将...
- 第三种方式是通过依赖注入来分离责任。它可以是通过协议关闭的依赖项,也可以是关闭(就像被签名关闭)一样的东西。最主要的是,新职责的执行应该离开主体。
在我们的情况下如何看待?例如,像这样(来自Vasily的决定):class Hero {
let weapon: Weapon
init (_ weapon: Weapon) {
self.weapon = weapon
}
func strike() {
weapon.strike()
}
var weight: Stone {
return meatbagWeight + pantsWeight + helmetWeight + weapon.weight
}
var spells: [Spells] {
return spellsWithoutWeapon + weapon.spells
}
}
您需要做什么?Ninjutsu-协议:protocol Weapon {
func strike()
var weight: Stone {get}
var spells: [Spells] {get}
}
协议实现示例:class Sword: Weapon {
func strike() {
}
var weight: Stone {
}
var spells: [Spells] {
}
}
同样,Sword类也适用于:Club,Bow,Pike等。“很容易看出”(c),在新架构中,适用于每种特定类型武器的所有代码都归为相应的类别,并且不会按照英雄与其他类型的武器一起传播。这使得阅读和理解英雄以及任何特定武器变得更加容易。此外,由于所施加协议的要求,在添加新型武器或向武器添加新功能时(例如,武器可能具有价格计算方法),追踪要实现的所有方法要容易得多。在这里,您可以看到依赖注入使Hero类对象的创建变得复杂。过去做的事情很简单:let lastHero = Hero()
现在,它变成了一套说明,可以在需要创建英雄的任何地方重复使用。但是,Vasily照顾了这一点:class HeroFactory {
static func makeSwordsman() -> Hero {
let weapon = Sword()
return Hero(weapon)
}
static func makeClubman() -> Hero {
let weapon = Club()
return Hero(weapon)
}
}
显然,瓦西里必须出汗才能散开Petya(和我)设计的堆积物。当然,查看最后一个解决方案,可能会产生以下想法:好吧,事实证明,规范。读取和扩展很方便,但是所有这些工厂/协议/依赖项都是一堆开销吗?一种代码,它在功能方面不提供任何内容,而仅用于组织代码。“用于组织代码的代码”,mgm。真的有必要随时随地围着这个花园吗?
对第一个问题的诚实回答是:是的,这是企业非常喜欢的功能的开销。
关于“何时?”的问题 答案部分:剑侠的哲学或“你什么时候必须统治?”
一开始是个剑士。在旧代码的意义上,这是很正常的。只要有一个英雄和一种武器,就无需区分它们-一样,英雄没有别的了。整体代码通过其文本证实了这一事实。剑客-听起来还不算太糟。最初的“天真”编辑会导致什么?if-chik的添加导致了什么?添加if-chic导致...一个突变!突变,即 可以在“人类”和“人类杜宾”之间变异的可变对象。同时,如果您在实施突变体时犯了一些错误,则会出现“人髓磷脂”状态。不要这样!根本不需要“人类杜宾”。有必要:- 人+对剑的依赖(需要剑);
- 人+对俱乐部的依赖(需要俱乐部)。
并非每种成瘾都是邪恶的!这种对酒精的依赖是邪恶的,礼节是好的。是的,甚至对物体的依赖都比“人类杜宾”好!突变何时发生?当添加标志时,会发生向突变体的转换:整体代码仍然是整体的,但是当标志被更改(变异)时,同一对象的行为开始发生显着变化。瓦西里很容易挑出两个阶段的突变:- 在标志中添加标志和第一个“ if”(或“ switch”或其他分支机制)。局势正在威胁,但可以忍受:英雄倾倒了放射性废料,但他克服了。
- «if» , . . — . , - - .
因此,在所考虑的情况下,为了防止发生技术债务,在经过第一阶段(最坏的情况下进入第二阶段)之前构建体系结构是有意义的。瓦西里到底做了什么治疗突变体?从技术角度来看-使用了注入依赖封闭协议。从哲学上讲-分担责任。该类作品的所有功能可以相互互换(这意味着它们是彼此替代的),因此已从英雄中删除。一种新的替代功能-剑的实施,球杆的实施-外观彼此不同,并且与以前一样不同没有其他英雄代码。因此,在“天真”代码中已经出现了一些新东西,它的替代行为与《英雄》无争议的部分有所不同。因此,在“天真”代码中,出现了对新业务实体的隐式描述:剑和棍棒,根据英雄所散布。为了方便与新的业务实体一起使用,有必要将它们区分为具有自己名称的独立代码实体。因此,存在责任分工。
PS TL; DR;- 看到标志了吗?
- 做个男人,该死!删除它!
- 成瘾注射
- ...
- 利润!11