基于装饰器——我劝你不要在业务代码上装逼!!!

这篇具有很好参考价值的文章主要介绍了基于装饰器——我劝你不要在业务代码上装逼!!!。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

基于装饰器——我劝你不要在业务代码上装逼!!!

装饰器模式的定义

  • 在传统的面向对象语言中,给对象添加功能常使用继承的方式,但继承的方式并不灵活,会带来一些许多问题,如:超类和子类存在强耦合性,也就是说当改变超类时,子类也需要改变。
  • 而装饰器模式的出现改变的这种方式,装饰器模式可在不改变现有对象解构的基础上,动态地为对象添加功能

传统的 JavaScript 装饰器

var plane = {
    fire: function () {
        console.log("普通子弹");
    },
};

var missleDecorator = function () {
    console.log("发射导弹");
};

var atomDecorator = function () {
    console.log("发射原子弹");
};

var fire1 = plane.fire;
plane.fire = function () {
    fire1();
    missleDecorator();
};

var fire2 = plane.fire;
plane.fire = function () {
    fire2();
    atomDecorator();
};

plane.fire();
/**
普通子弹
发射导弹
发射原子弹
 */

装饰函数

  • 在 JavaScript 中,几乎一切都是对象,其中函数也被成为对象,在平时的开发中,我们都在和函数打交道。在给对象扩展属性和方法时,很难在不改动原功能函数的情况下,给函数添加一些额外的功能,最直接的粗暴方式就是直接改写函数,但这是最差的方式,这违反了开放——封闭原则。
  • 如下:
function a(){
    console.log(1);
}

// 改写:
function a(){
    console.log(1);

    // 新功能
    console.log(2);
}
  • 很多时候,我们都不想去触碰之前的一些代码,但需要添加功能,所以如果需要在不改变原功能函数的情况下,给函数添加功能。可使用以下方式:
  • 要想完美的给函数添加功能,可使用 AOP 来装饰函数
    • AOP:一种编程规范,通过将关注点从主业务逻辑中剥离出来并单独处理,以此来提高代码的可读性和重用性。
  • 如下:
Function.prototype.before = function (beforeFn) {
    var _self = this;
    return function () {
        beforeFn.apply(this, arguments);
        return _self.apply(this, arguments);
    };
};

Function.prototype.after = function (afterFn) {
    var _self = this;
    return function () {
        var ret = _self.apply(this, arguments);
        afterFn.apply(this, arguments);
        return ret;
    }
}

// before 和 after 函数都接收一个函数作为参数,这个函数也就是新添加的函数(里面也就是要添加的新功能逻辑)。
// 而before 和 after 函数区别在于在是原函数之前执行还是之后执行。
  • AOP 函数的使用
Function.prototype.before = function (beforeFn) {
    var _self = this;
    return function () {
        beforeFn.apply(this, arguments);
        return _self.apply(this, arguments);
    };
};

Function.prototype.after = function (afterFn) {
    var _self = this;
    return function () {
        var ret = _self.apply(this, arguments);
        afterFn.apply(this, arguments);
        return ret;
    }
}

var o1 = function(){
    console.log('1');
}
var o2 = function(){
    console.log('2');
}
var o3 = function(){
    console.log('3');
}

var desctor = o1.after(o2);
desctor = desctor.after(o3);
desctor(); // 1 2 3
/**
var desctor = o1.after(o2);
desctor = desctor.after(o3);
desctor();
1
2
3

var desctor = o1.before(o2);
desctor = desctor.before(o3);
desctor();
3
2
1

var desctor = o1.after(o2);
desctor = desctor.before(o3);
desctor();
3
1
2


var desctor = o1.before(o2);
desctor = desctor.after(o3);
desctor();
2
1
3
 */

AOP的应用

1.数据上报
  • 在程序开发中,当业务代码开发完后,在结尾时需要加很多的日志上报的代码,普遍我们会去改已经之前封装好的功能函数。其实这并不是一个好的方式,那如何在不直接修改之前函数的基础上添加日志上报功能呢?
  • 如下:
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>AOP日志上报</title>
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <script src="https://unpkg.com/vue@3.2.20/dist/vue.global.js"></script>
    </head>
    <body>
        <div id="app">
            <button class="btn" @click="handler">Button</button>
            <p id="tt">{{message}}</p>
        </div>
    </body>
</html>
<script type="text/javascript">
    // log report
    const { reactive, ref, createApp } = Vue;
    const app = createApp({
        setup() {
            const message = ref("未点击");
            const count = ref(0);

            Function.prototype.before = function (beforeFn) {
                var _self = this;
                return function () {
                    beforeFn.apply(this, arguments);
                    return _self.apply(this, arguments);
                };
            };

            Function.prototype.after = function (afterFn) {
                var _self = this;
                return function () {
                    var ret = _self.apply(this, arguments);
                    afterFn.apply(this, arguments);
                    return ret;
                };
            };

            function handler() {
                message.value = `已点击${++count.value}`;
            }

            handler = handler.after(log);

            function log() {
                message.value = message.value + "-----> log reported";
                console.log("log report");
            }

            return {
                message,
                handler,
            };
        },
    });
    app.mount("#app");
</script>
2.动态参数
  • 在日常开发中,我们需要向后台接口发送请求来获取信息,例如传参如下。业务在后续时需要添加新参数,每个接口需要把 token 值也一并传过去, 普遍我们会去改封装的请求方法,把 token 参数添加进去。但我们直接修改封装好的请求方法不是好的行为,那我们可使用上面说过的 AOP 方式来改进。
{
    name: 'xxxx',
    password: 'xxxx',
}
  • 如下:
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>AOP动态参数</title>
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <script src="https://unpkg.com/vue@3.2.20/dist/vue.global.js"></script>
    </head>
    <body>
        <div id="app">{{message}}</div>
    </body>
</html>

<script type="text/javascript">
    const { reactive, ref, createApp } = Vue;
    const app = createApp({
        setup() {
            const message = ref("empty params");
            Function.prototype.before = function (beforeFn) {
                var _self = this;
                return function () {
                    beforeFn.apply(this, arguments);
                    return _self.apply(this, arguments);
                };
            };

            Function.prototype.after = function (afterFn) {
                var _self = this;
                return function () {
                    var ret = _self.apply(this, arguments);
                    afterFn.apply(this, arguments);
                    return ret;
                };
            };

            function ajax(type, url, params){
                message.value = `${type} ----> ${url} -----> ${JSON.stringify(params)}`;
            }

            function getToken(){
                // do something
                return 'token';
            }

            ajax = ajax.before(function(type, url, params){
                params.token = getToken();
            })

            ajax('get', 'https://www.baidu.com/userinfo', {name: 'se', password: 'xsdsd'});
            return {
                message,
            };
        },
    });
    app.mount("#app");
</script>
3.表单校验
  • 在日常开发中,我们经常要去做校验表单数据,通常的方式是在功能函数中进行判断处理或将判断逻辑提取为一个函数的方式。但这种方式其实是与功能性函数相混合,且校验逻辑与功能性函数有耦合关系。那我们可使用 AOP 方式来改进。
  • 如下:
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>AOP表单验证</title>
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <script src="https://unpkg.com/vue@3.2.20/dist/vue.global.js"></script>
    </head>
    <body>
        <div id="app">
            <label>
                姓名:
                <input
                    type="text"
                    v-model="data.name"
                    placeholder="请输入姓名"
                />
            </label>
            <label>
                密码:
                <input
                    type="text"
                    v-model="data.pass"
                    placeholder="请输入密码"
                />
            </label>
            <p v-if="data.name || data.pass">{{data.name + '/' + data.pass}} ----after-----> {{data.message}}</p>
            <hr>
            <button @click="submitBtn">submit</button>
        </div>
    </body>
</html>

<script type="text/javascript">
    const { reactive, ref, createApp, watchEffect } = Vue;
    const app = createApp({
        setup() {
            const data = reactive({
                name: "",
                pass: "",
                message: "",
            });

            Function.prototype.before = function (beforeFn) {
                var _self = this;
                return function () {
                    if (beforeFn.apply(this, arguments) === false) return;
                    return _self.apply(this, arguments);
                };
            };

            function valid() {
                if (!data.name || !data.pass) {
                    alert("用户名或密码不能为空");
                    return false;
                }
            }

            function formSubmit() {
                console.log("data ------>", data);
                data.message = `${data.name} ------- ${data.pass}`;
            }

            formSubmit = formSubmit.before(valid);

            function submitBtn() {
                formSubmit();
            }
            return {
                data,
                submitBtn,
            };
        },
    });
    app.mount("#app");
</script>

装饰器模式的优缺点

  • 优点:
    1. 扩展性强:装饰器模式允许在不修改现有代码的情况下,动态地添加新功能或修改现有功能。通过使用装饰器,可以在运行时按需组合和堆叠装饰器对象,实现各种组合方式,从而实现更多的功能扩展。
    2. 遵循开闭原则:装饰器模式通过添加装饰器类来扩展功能,而不是修改现有的代码。这样可以保持原有代码的稳定性,符合开闭原则,即对扩展开放,对修改关闭。
    3. 分离关注点:装饰器模式将功能的扩展和核心功能分离开来,每个装饰器类只关注单一的额外功能。这样可以使代码更加清晰、可读性更高,并且容易维护和测试。
  • 缺点:
    1. 增加复杂性:使用装饰器模式会增加额外的类和对象,引入了更多的复杂性和层次结构。这可能使代码变得更加复杂,理解和调试起来可能更加困难。
    2. 潜在的性能影响:由于装饰器模式涉及多个对象的组合和堆叠,可能会引入额外的运行时开销,对性能产生一定的影响。尤其是当装饰器链较长时,可能会导致性能下降。

装饰器模式的适用场景

  1. 动态地扩展对象功能:当需要在运行时动态地为对象添加额外的功能或责任时,装饰器模式是一个很好的选择
  2. 遵循开闭原则:如果你希望在不修改现有代码的情况下扩展功能,而且要保持代码的稳定性,装饰器模式是一个合适的解决方案。
  3. 分离关注点:当你希望将不同的功能分离开来,使每个功能都有自己独立的装饰器类时,装饰器模式是有用的。每个装饰器只关注单一的额外功能,这样可以使代码更加清晰、可读性更高,并且容易维护和测试。
  4. 多层次的功能组合:如果你需要实现多个功能的组合,而且每个功能都可以灵活选择是否添加,装饰器模式可以很好地满足这个需求。通过堆叠多个装饰器对象,可以按照特定的顺序组合功能,实现各种组合方式。
  5. 继承关系的替代方案:当你面临类似于创建大量子类的情况时,装饰器模式可以作为继承关系的替代方案。通过使用装饰器模式,可以避免创建过多的子类,而是通过组合不同的装饰器来实现不同的功能组合。
Tip: 文章部分内容参考于曾探大佬的《JavaScript 设计模式与开发实践》。文章仅做个人学习总结和知识汇总

文章来源地址https://www.toymoban.com/news/detail-684090.html

到了这里,关于基于装饰器——我劝你不要在业务代码上装逼!!!的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 自学网络安全, 一般人我劝你还是算了吧

    1.这是一条坚持的道路,三分钟的热情可以放弃往下看了. 2.多练多想,不要离开了教程什么都不会了.最好看完教程自己独立完成技术方面的开发. 3.有时多 google,baidu,我们往往都遇不到好心的大神,谁会无聊天天给你做解答. 4.遇到实在搞不懂的,可以先放放,以后再来解决. 目录:

    2024年02月07日
    浏览(37)
  • 自学黑客(网络安全),一般人我劝你还是算了!

    网络安全在当今信息社会越来越受到重视,但不同于Java、C/C++等后端开发岗位有非常明晰的学习路线,网络安全更多是靠自己摸索,要学的东西又杂又多,难成体系。   这也是为什么我经常劝别人不要入网络安全的坑,因为一般人真坚持不下去! 但考虑到部分同学仍然对网

    2024年02月08日
    浏览(37)
  • 自学网络安全(黑客),一般人我劝你还是算了吧

    一、自学网络安全学习的误区和陷阱 1.不要试图先成为一名程序员(以编程为基础的学习)再开始学习 我在之前的回答中,我都一再强调不要以编程为基础再开始学习网络安全,一般来说,学习编程不但学习周期长,而且实际向安全过渡后可用到的关键知识并不多 一般人如

    2024年02月06日
    浏览(47)
  • 自学黑客(网络安全),一般人我劝你还是算了吧

    一、自学网络安全学习的误区和陷阱 1.不要试图先成为一名程序员(以编程为基础的学习)再开始学习 我在之前的回答中,我都一再强调不要以编程为基础再开始学习网络安全,一般来说,学习编程不但学习周期长,而且实际向安全过渡后可用到的关键知识并不多 一般人如

    2024年02月03日
    浏览(60)
  • 自学网络安全(黑客)?一般人我劝你还是算了吧!

    我在之前的回答中,我都一再强调不要以编程为基础再开始学习网络安全,一般来说,学习编程不但学习周期长,而且实际向安全过渡后可用到的关键知识并不多 一般人如果想要把编程学好再开始学习网络安全往往需要花费很长时间,容易半途而废。而且学习编程只是工具不

    2024年02月07日
    浏览(40)
  • 自学网络安全【黑客】,一般人我劝你还是算了吧

    前言:我是劝一般人算了,看你是一般人还是。。。 一、网络安全学习的误区 1.不要试图以编程为基础去学习网络安全 2.不要刚开始就深度学习网络安全 3.收集适当的学习资料 4.适当的报班学习 二、学习网络安全的些许准备 1.硬件选择 2.软件选择 3.外语能力 三、网络安全学

    2024年02月04日
    浏览(48)
  • 自学黑客【网络安全】,一般人我劝你还是算了吧

    1.不要试图先成为一名程序员(以编程为基础的学习)再开始学习 我一直强调不要以编程为基础再开始学习网络安全,一般来说,学习编程不但学习周期长,而且实际向安全过渡后可用到的关键知识并不多 一般人如果想要把编程学好再开始学习网络安全往往需要花费很长时间

    2024年02月05日
    浏览(55)
  • 【华为机试】死记硬背没思路?一般人我劝你还是算了吧

    大家好,我是哪吒。 五月份之前,如果你参加华为OD机试,收到的应该是2022Q4或2023Q1,这两个都是A卷题。 5月10日之后,很多小伙伴收到的是B卷,那么恭喜你看到本文了,抓紧刷题吧。B卷新题库正在更新中 。 华为机试有三道题,第一道和第二道属于简单题,分值为100分,第

    2024年02月12日
    浏览(34)
  • 自学黑客(网络安全),一般人我劝你还是算了吧(自学网络安全学习路线--第一章 网络协议基础 )

    1.不要试图先成为一名程序员(以编程为基础的学习)再开始学习 我在之前的回答中,我都一再强调不要以编程为基础再开始学习网络安全,一般来说,学习编程不但学习周期长,而且实际向安全过渡后可用到的关键知识并不多 一般人如果想要把编程学好再开始学习网络安全

    2024年02月11日
    浏览(41)
  • 基于短信宝API零代码实现短信自动化业务

    基于短信宝开放的API能力,实现在特定事件(如天气预警)或定时自动发送短信(本文以定时群发短信为例)。通过Aboter平台如何实现呢? 首先创建一个IPaaS流程,触发条件组件编辑区选择【定时触发】类型。比如早上10点发送短信通知。 拖动左侧【应用连接器 短信邮件 短

    2024年02月14日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包