前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >每位前端开发者都应该知道的方法论:JavaScript 中的 DILOS 原则

每位前端开发者都应该知道的方法论:JavaScript 中的 DILOS 原则

作者头像
深度学习与Python
发布2020-11-06 11:49:13
5200
发布2020-11-06 11:49:13
举报
文章被收录于专栏:深度学习与python

作者 | Kealan Parr

译者 | 王强

策划 | 李俊辰

本文最初发布于 hackernnon 网站,经原作者授权由 InfoQ 中文站翻译并分享。

SOLID 原则是开发人员创建灵活、可理解和可维护代码的基础。但你要正确遵循这些原则就可能明显减慢开发速度,并且大多数人没那么操心代码质量,因此我发明了一套更好用的原则。

DILOS 原则是我们构建可怕代码的坚实支柱。

我个人已经用上了 DILOS 原则,成功创建出大堆混乱难懂和臃肿的代码,这篇文章我就来具体介绍一下:

DILOS 的含义:

  • D——依赖倒置反转原则
  • I——接口捆绑原则
  • L——里氏分离原则
  • O——开闭原则
  • S——多职责原则

依赖倒置反转原则

高级模块必须依赖低级模块。依赖实体而不是抽象。

把某些东西抽象出来,就是要隐藏这些东西内部的实现细节,有时是原型,有时是函数。因此当你调用这个函数时不必完全了解其机制。如果你非得先搞懂大型代码库中的所有函数,那就别想着写代码了。可能需要几个月的时间才能看完那些东西。

但现在我们要把这条原则倒过来:不要抽象任何东西。也就是尽量少用小块函数,把所有东西都塞到一个单体函数里。如果别人想调用你的函数,让他看懂你的每一行代码再说吧。

下面是整洁代码的一个示例:

代码语言:javascript
复制
function hitAPI(url, httpMethods){
    // Implementation example
}
hitAPI("https://www.kealanparr.com/retrieveData", "GET");
hitAPI("https://www.kealanparr.com/retrieveInitialData", "GET");

你看,你用不着操心 hitAPI 在做什么,我们只传递了一个 URL 和一个 HTTP 请求,然后就搞定了。现在这段代码是高度可重用和可维护的。这个函数可以在一个地方处理所有 URL。我们已经尽可能让高级函数(我们放在 base 原型中的函数,可以和下层的许多东西共享)不依赖于任何低级函数。

那么如果我们反转这个依赖倒置(Dependency-Inversion)原则呢?

代码语言:javascript
复制
function hitDifferentAPI(type, httpMethods){
    if (this instanceof initialLoad) {
        // Implementation example
    } else if (this instanceof navBar) {
        // Implementation example
    } else {
        // Implementation example
    }
}

现在我们让高级 api 请求依赖于许多较低级别的类型。完成任务后,它不再是完全通用的了,并且会依赖其继承链中较低的类型。

接口捆绑原则

强迫客户端依赖它们不用的 [代码]。

其他语言里的接口用于定义不同对象拥有的方法和属性。

不要向不需要的对象添加代码?不要将太多无关的功能捆绑在一起?嗯,胡扯嘛这是。

我们一定要把松散耦合的代码都绑在一个地方。关键在于一定要依赖你用不着的东西。

我们稍后将在“多重职责原则”中进一步解释,但请记住这条原则,在所有地方疯狂用它。还记得花半天时间查找几百个文件搜索 bug 的经历吗?那种事情不会再有了。搞一个名为 main.js 的 JS 文件,然后把所有代码都塞进去。

让你的站点预加载所有内容,不要搞什么 JS 脚本按需加载,这样初始加载速度就会慢如蜗牛啦。

写代码的时候把宇宙毁灭时的需求都想好,然后提前写好对应的逻辑,反正你迟早用得上嘛。

如果有人需要你代码里的一根香蕉,那就塞给他一头拿着香蕉的大猩猩。客户端要啥就给它附送一堆垃圾,它们肯定会感谢你的。

写的函数越少越好。把什么东西封装在一个放在其他地方的新函数里,并抽象化它的逻辑?可别这么干。怎么让人犯迷糊怎么来,需要代码的时候复制粘贴过来就行。

理想情况下,我们的代码流只有 1 个对象。在非常大的代码库中,我们可能有 2 个对象。通常将其称为“上帝对象”反模式,其中我们要到处用单独的一个对象,因为所有事情都得它来做。稍后我们将详细讨论。

里氏分离原则

软件各部分的子级和父级不可以互换。

你竟然会在代码中使用继承吗?这绝对要注意。你应该复制粘贴而不是继承代码。Copy-Paste 反模式就是这个意思,你不应该把代码的通用功能抽象为模块化的可重用功能,而应当在所有需要的地方都复制代码。这会增加技术债(将来你迟早要回来修复的),而且每更改一段代码,都需要多次搜索才能找到它在代码库中出现在了哪些位置。

DRY 原则表示 Don't Repeat Yourself,而 WET 原则恰恰相反,指的是我们 Write Everything Twice。必要时应该写更多次数。抵制继承,随意复制粘贴。

但是,如果你确实需要利用里氏分离原则,请确保在与继承链中较高子级(父级)交换对象时,继承链中较低子级的对象原型不能正常工作。

为什么?

因为如果我们不遵循里氏分离原则,我们就会构建准确而健壮的继承链。将逻辑抽象为封装好的 base 原型 / 对象。我们还会对原型链中的不同方法按逻辑分组,它们的特定覆盖会让代码路径更加可预期和可发现。

如果你正确地遵循了这一原则,那么父级能用的时候子级也没法用,继承就会毫无意义。如果你的程序尝试引用的函数并不存在于自己的子级中,因此崩溃掉——你就会完全避免继承会给你带来的任何好处——这正是我们遵循这一原则的目的。

开闭原则

对象应对修改开放,对扩展封闭。

好的代码通常会扩展对象的代码,以限制修改 base 原型。这样,完成扩展的对象就可以处理自己的状态以及需要执行的新功能(仅处理子项需要做的少量更改即可)。

上面这段话是胡说八道,我们真正应该做的是:

  • 对修改开放——要做任何更改,我们都要更改 base 原型 / 函数。
  • 对扩展封闭——如果要扩展,你就会开始模块化代码,不要这么做。确保所有事情都在 base 原型 / 函数中完成。

如果在处理每个场景时都分支,那么上面这两步带来的负担就更重了。所以最后你会看到诸如这样的代码:

代码语言:javascript
复制
function makeSound(animal) {
    if (animal == "dog") {
        return "bark";
    } else if (animal == "duck") {
        return "quack";
    } else if (animal == "cat") {
        return "meow";
    } else if (animal == "crow") {
        return "caw";
    } else if (animal == "sheep") {
        return "baa";
    } else if (animal == "cow") {
        return "moo";
    } else if (animal == "pig") {
        return "oink";
    } else if (animal == "horse") {
        return "neigh";
    } else if (animal == "chicken") {
        return "cluck";
    } else if (animal == "owl") {
        return "twit-twoo";
    } else {
        /// It has to be a human at this point
        return "hi";
    }
}

现在,如果你需要更改某些内容,只需添加另一个 if 检查即可。这和下文列出的多职责原则有关,但核心在于将所有内容都包含在 base 函数 / 原型中。

这样你就会进入“脆弱基类”反模式。在这种模式下,更改 base 函数 / 原型时,你最后会在调用此函数的其他触点上出现错误。

例如,假设 human 不该再掉进 else 里,而你添加了新的 animal,名为 wolf,你就会引入错误(除非你更新了期望 human 被记录的位置)。

多职责原则

确保你的函数 / 对象有多重职责。

优秀的编程人员常常会将他们的代码分成多个不同的对象或模块。但我可搞不清楚这种事情,我记不住它们都负责什么事情。

下面是一个例子:

代码语言:javascript
复制
const godObject = {
    handleClicks: function(){},
    getUserName: function(){},
    handleLogin: function(){},
    logTransactionId: function(){},
    initialSiteLoad: function(){} 
};

godObject 负责网站的许多功能。付款、登录、网站负载、记录交易 ID 以及网站上的所有点击功能都包括在其中。太棒了,这样如果你遇到了错误,就会知道它只能出现在这里。确保代码库中的每个地方都需要访问 godObject。让它做一切工作。

我们在代码中真正想要的是高耦合(确保系统的各个部分相互依赖)和低内聚(将许多随机的数据和片段放在一起)。

这种反模式有时被称为“瑞士军刀”,因为就算你要的只是一把剪子,但它也可以是指甲锉、锯子、镊子、开瓶器,也可以是软木钉。

我不断强调的是,要把所有东西放在一起,然后绑定、打包、捆在一起。

总 结

我希望大家看完这篇文章后,就知道软件究竟应该怎么写才能尽可能增加调试需求、尽可能把人搞糊涂,并且搞出来最多的技术债。

延伸阅读:

https://hackernoon.com/introducing-dilos-principles-for-javascript-code-jp1d3w1b

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-11-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 InfoQ 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档