提高代码质量——AOP面向切面编程

王元肉==>个人主页

大家一起加油,我喜欢分专栏将知识打碎成一份一份小知识点,一篇文章只说一个知识点,因此每篇文章篇幅较短,希望大家不要介意。如有需要可以查看专栏看看是否有该文章中涉及到的知识点进行讲解的文章,如果大家觉得有帮助,希望大家三连支持一下。

什么是面向切面编程

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译
方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件
开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,
提高程序的可重用性,同时提高了开发的效率。--来源百度百科

我们为什么要用面向切面编程

实际业务代码感受不足

面向切面编程的好处是什么,我们为什么要使用它。如百度百科中所说一样
“对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,
提高程序的可重用性,同时提高了开发的效率”
用文字描述太干巴了。还是直接上代码带大家直观感受一下。
// 你接手的代码中可能会有这么一段代码
function People(name) {
    this.name = name;
}
People.prototype.getOffWork = function() {
    console.log("下班回家");
};
People.prototype.eat = function() {
    console.log("吃饭");
};
People.prototype.lave = function() {
    console.log("洗澡");
};
People.prototype.sleep = function() {
    console.log("睡觉到天亮");
};

const company = {
    name: "madouchuanmei",
    employees: [new People("zhangsan"), new People("lisi"), new People("wangwu")],
    // 打卡
    clock: function(name) {
        for (const iterator of this.employees) {
            if (iterator.name = name) {
                console.log(iterator.name);
                iterator.getOffWork();
                return iterator;
            }
        }
    }
};

// 张三打卡下班 吃饭洗澡睡觉
const zhangsan = company.clock("zhangsan");
zhangsan.eat();
zhangsan.lave();
zhangsan.sleep();
// --------------------------------

在这里插入图片描述

咋一看没啥问题,还是一样的套路,我们不能只看当前,业务少,要是业务多起来,我们写了一堆obj.xxx(),obj.xxx()
也不雅观不是,再者大家应该都知道jquery用到了链式调用,jquery为什么要用链式调用呢?我们一起来看看。
// 在jquery中你可能会写出类似于这样一段代码
$("#Test").addClass('style').find("div").eq(0).fadeOut(200);
// 假设jquery不支持链式调用会怎么样?我们的代码可能会变成这样
$("#Test").addClass('style');
$("#Test").find("div");
$("#Test").eq(0);
$("#Test").fadeOut(200);
/* 代码一点也不整洁,并且每次调用一次方法都需要执行一次$("#Test")去遍历DOM树节点查询我们
要的节点代码一点都不简洁 */
同理,我们上面的代码中的代码也是不整洁的,并且我们需要一个额外zhangsan变量来存储这个person,有多少
员工打卡下标就会创建多少个变量,占用一些额外非必须的内存。那么我们怎么改进上面那段代码呢?当然是参考jquery实现函数的链式调用啦。

优化1减少内存占用与性能消耗,简洁化代码

function People(name) {
    this.name = name;
}
People.prototype.getOffWork = function() {
    console.log("下班回家");
    return this;
};
People.prototype.eat = function() {
    console.log("吃饭");
    return this;
};
People.prototype.lave = function() {
    console.log("洗澡");
    return this;
};
People.prototype.sleep = function() {
    console.log("睡觉到天亮");
    return this;
};

const company = {
    name: "madouchuanmei",
    employees: [new People("zhangsan"), new People("lisi"), new People("wangwu")],
    // 打卡
    clock: function(name) {
        for (const iterator of this.employees) {
            if ((iterator.name = name)) {
                console.log(iterator.name);
                return iterator.getOffWork();
            }
        }
    },
};

// 张三打卡下班 吃饭洗澡睡觉
company.clock("zhangsan").eat().lave().sleep();
// --------------------------------

在这里插入图片描述

优化2增强程序的健壮性,避免人为bug

怎么样,大家感受一下,是不是节省了一个变量占用内存的空间且调用代码变得简洁了。
but,希望大家能够细心一点观察,这几个事件有什么关系?我们可以先睡觉到天亮再吃饭和洗澡吗?(ps:
假设睡觉到天亮直接就要上班了,不考虑早上起床洗澡的人...例子可能举的有点牵强,大家见谅)实际开发时,
一定有一些业务之间存在先后顺序的,这时候,我们自己编写的,可能不会出现手动调错顺序,但是整个程序代码
这么写了,不管是自己还是后来者都是可能会出现人为调用顺序错误导致bug出现的这种风险的,此时显然这段代码
还是存在缺陷的,那我们应该怎么做?别急,看我操作。
// 作为链式调用函数的一个桥梁
// 作为链式调用函数的一个桥梁
function People(name) {
    this.name = name;
}
People.prototype.getOffWork = function() {
    console.log("下班回家");

    function fn() {}
    fn.lock = false;
    return fn;
};
People.prototype.eat = function() {
    const _this = this;
    if (_this.lock) {
        console.log("warning,sleep没有写在最后");

        function fn() {
            console.log("吃饭");
            _this();
        }
        fn.lock = true;
        return fn;
    } else {
        function fn() {
            _this();
            console.log("吃饭");
        }
        fn.lock = false;
        return fn;
    }
};
People.prototype.lave = function() {
    const _this = this;
    if (_this.lock) {
        console.log("warning,sleep没有写在最后");

        function fn() {
            console.log("洗澡");
            _this();
        }
        fn.lock = true;
        return fn;
    } else {
        function fn() {
            _this();
            console.log("洗澡");
        }
        fn.lock = false;
        return fn;
    }
};
People.prototype.sleep = function() {
    const _this = this;

    function fn() {
        _this();
        console.log("睡觉到天亮");
    }

    /* 走到sleep将lock改为true,告诉后面的调用者
      前面的函数中包含sleep函数,你们要执行在我之前 */
    fn.lock = true;
    return fn;
};

Function.prototype.getOffWork = People.prototype.getOffWork;
Function.prototype.eat = People.prototype.eat;
Function.prototype.lave = People.prototype.lave;
Function.prototype.sleep = People.prototype.sleep;

const company = {
    name: "madouchuanmei",
    employees: [new People("zhangsan"), new People("lisi"), new People("wangwu")],
    // 打卡
    clock: function(name) {
        for (const iterator of this.employees) {
            if ((iterator.name = name)) {
                console.log(iterator.name);
                return iterator.getOffWork();
            }
        }
    },
};

// 张三打卡下班 吃饭洗澡睡觉
company.clock("zhangsan").sleep().eat().lave()();
company.clock("zhangsan").eat().sleep().lave()();
company.clock("zhangsan").eat().lave().sleep()();
company.clock("zhangsan").lave().eat().sleep()();
// --------------------------------

在这里插入图片描述

我们发现当sleep按照正确顺序书写时,sleep永远都是在最后执行且执行顺序按照
执行链调用。
当sleep写在前面调用了,会提示我们sleep没有写在最后,我在这里处理是程序依然
可以执行,sleep依然还是在最后执行,但是别的方法不再按照调用链执行了(这点
我优化不了了,有大佬搞起来了请务必指点一下小弟),当然实际开发时,可以直接
给出报错,在编译就发现sleep没有写在最后这个问题。

小例子加深概念

demo1

/* 
    确保链式函数的参数函数可以拿到调用者传递的参数
*/
Function.prototype.before = function(fnBe) {
    const _this = this; // this 指向test对象
    // 返回一个函数,为了链式调用
    return function() {
        // console.log(this, "before");
        fnBe.call(this, ...arguments);
        _this.call(this, ...arguments);
    };
};

Function.prototype.after = function(fnAf) {
    const _this = this; // this 指向before返回的那个函数
    return function() {
        // console.log(this, "after");
        // console.log(arguments);
        _this.call(this, ...arguments);
        fnAf.call(this, ...arguments);
    };
};

function test() {
    console.log("test");
    // console.log(this);
    console.log(...arguments);
}

test
    .before(function() {
        console.log("before");
        console.log(this); // this指向test调用者
        console.log(...arguments);
    })
    .after(function() {
        console.log("after");
        console.log(this); // this指向test调用者
        console.log(...arguments);
    })
    .call(test, "a", "b", "c");

demo2

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.26.0/axios.js"></script>
    <script>
        // 我们为自己本地开发时创建一个网络请求日志功能
        Function.prototype.before = function(fnBe) {
            const _this = this;
            return function() {
                fnBe.call(this, ...arguments);
                _this.call(this, ...arguments);
            };
        };

        Function.prototype.after = function(fnAf) {
            const _this = this;
            return function() {
                _this.call(this, ...arguments);
                // 
                const inter = setInterval(() => {
                    if (this.result !== undefined) {
                        fnAf.call(this, this.result)
                        clearInterval(inter);
                    }
                }, 1000)
            };
        };

        function network(method, url, data = {}) {
            axios({
                method,
                url,
                data
            }).then(res => {
                this.result = res;
            }).catch(err => {
                this.result = err;
            })
        }

        network
            .before(function() {
                /* 可以获取到每次请求的方式,路径,参数 */
                console.log(...arguments);
            })
            .after(function() {
                /* 可以获取每次网络请求的成功响应体或者失败的err */
                console.log(...arguments);
            })
            .call(network, "get", "http://jsonplaceholder.typicode.com/posts");
    </script>
</body>

</html>

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LiuJie_Boom

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值