>웹 프론트엔드 >JS 튜토리얼 >Javascript의 AOP 프로그래밍

Javascript의 AOP 프로그래밍

伊谢尔伦
伊谢尔伦원래의
2016-11-23 11:14:141088검색

덕펀치

먼저 AOP 프로그래밍에 대해 이야기하지 말고, 덕펀치 프로그래밍부터 시작해보자.

위키피디아에서 덕펀치라고 검색하면 꼭 찾아야 할 항목이 몽키패치입니다. 설명에 따르면 몽키패치(monkey patch)라는 단어는 작전 중 조용히 코드를 바꾼다는 뜻의 게릴라 패치(guerrilla patch)에서 유래됐으며 게릴라(guerrilla)라는 단어는 고릴라와 발음이 같고, 후자는 원숭이(전자는 '고릴라'라는 뜻)와 비슷하다는 뜻이다. 마침내 For Monkey 패치로 진화했습니다.

덕펀치(Duck Punch)를 들어본 적이 없다면, 덕타이핑(Duck Typing)은 들어보셨을 겁니다. 유명한 예를 들면, 오리를 식별하는 방법:

나는 오리처럼 걷고, 오리처럼 헤엄치고, 오리처럼 꽥꽥거리는 새를 보면 그 새를 오리라고 부릅니다.

네, 오리처럼 꽥꽥거리고 오리처럼 헤엄치는 동물을 찾으면 그건 오리일 거예요!

이 테스트는 다소 뻔하고 무의미해 보일 수도 있지만 매우 실용적입니다. 그리고 프로그래밍의 문제 유형(Javascript 또는 유사한 동적 언어의 경우 "인터페이스" 또는 "기본 클래스"를 구현하는 방법)을 해결하는 데 사용할 수 있습니다. 과거에 대해서는 전혀 신경 쓸 필요가 없습니다. 메소드 유형이나 매개변수가 사용할 때 필요한지 여부만 관심이 있습니다.

var quack = someObject.quack;
 
if (typeof quack == "function" && quck.length == arguLength)
{
    // This thing can quack
}

너무 멀리 가고 있습니다. 표현하고 싶은 것은 오리 펀치입니다. 사실은 오리 타이핑에서 진화한 것입니다.

오리처럼 걷고 오리처럼 말하면 오리겠죠? 그럼 이 오리가 원하는 소음을 내지 않는다면, 아주 생생한 농담이 떠오를 때까지 오리를 주먹으로 때리면 됩니다.

미국, 홍콩, 중국 본토에서 경찰의 힘을 시험하기 위해서입니다. , 유엔은 세 명의 경찰관 중 누가 먼저 토끼를 찾을 수 있는지 알아보기 위해 세 개의 숲에 세 마리의 토끼를 배치했습니다. 과제: 토끼를 찾아보세요. (가운데는 생략...) 결국 어느 나라 경찰 4명만 있었는데, 그들은 하루 동안 마작을 하다가 해질녘에 각자 지휘봉을 들고 비명소리를 들었습니다. 숲에서 나오는 동물들. 그는 담배를 피우며 이야기를 나누며 웃었고, 그 뒤에는 코가 멍들고 얼굴이 부어오른 곰이 죽어가며 말했습니다. "더 이상 싸우지 마세요. 토끼..."

덕펀치는 다소 폭력적이지만 효과적인 방법이다. 코드 구현에 있어서는 원본 코드가 필요한 기능과 호환되도록 만드는 것을 의미합니다. 예를 들어 Paul Irish의 블로그에 있는 다음 예는 다음과 같습니다.

동시에 오리 펀치 모드를 되돌릴 수 있지만 이는 다음과 같습니다.
/**
    我们都知道jQuery的`$.css`方法可以通过使用颜色的名称给元素进行颜色赋值。
    但jQuery内置的颜色并非是那么丰富,如果我们想添加我们自定义的颜色名称应该怎么办?比如我们想添加`Burnt Sienna`这个颜色
*/
 
(function($){
     
    // 把原方法暂存起来:
    var _oldcss = $.fn.css;
 
    // 重写原方法:
    $.fn.css = function(prop,value){
 
        // 把自定义的颜色写进分支判断里,特殊情况特殊处理
        if (/^background-?color$/i.test(prop) && value.toLowerCase() === 'burnt sienna') {
           return _oldcss.call(this,prop,'#EA7E5D');
 
        // 一般情况一般处理,调用原方法
        } else {
           return _oldcss.apply(this,arguments);
        }
    };
})(jQuery);
 
// 使用方法:
jQuery(document.body).css('backgroundColor','burnt sienna')

하지만 거기에는 문제가 있습니다. 원래 메서드를 수정해야 합니다. 이는 확장을 위해서는 개방하고 수정을 위해서는 폐쇄해야 한다는 '개방-폐쇄' 원칙을 위반하는 것입니다. 이 문제를 해결하는 방법? AOP 프로그래밍을 사용하십시오.
(function($){
 
    var _old = $.fn.method;
 
    $.fn.method = function(arg1,arg2){
 
        if ( ... condition ... ) {
           return  ....
        } else {           // do the default
           return _old.apply(this,arguments);
        }
    };
})(jQuery);

AOP

시작하기

AOP는 Aspect 지향 프로그래밍을 의미하며 이는 분명히 객체 지향 프로그래밍과 관련이 있습니다. Aspect는 "aspect" 또는 "side"로 번역될 수 있으므로 AOP는 관점 지향 프로그래밍입니다.

측면을 어떻게 이해하나요?

객체 지향 프로그래밍에서 우리가 정의하는 클래스는 일반적으로 도메인 모델이며, 해당 클래스가 갖는 메서드는 일반적으로 순수한 비즈니스 로직과 관련됩니다. 예:

그러나 일반적으로 실제 상황은 더 복잡합니다. 예를 들어 결제 방법에 승인 감지를 추가하거나 통계를 위한 로그 또는 내결함성 코드를 보내야 합니다. 그러면 코드는 다음과 같이 됩니다.
Class Person
{
    private int money;
    public void pay(int price)
    {
         this.money = this.money - price;  
    }
}

더 무서운 점은 다른 메서드에도 비슷한 코드를 추가해야 한다는 점입니다. 이렇게 하면 코드의 유지 관리성과 가독성이 큰 문제가 됩니다. 이렇게 분산되어 있지만 공통적으로 사용되는 비업무용 코드를 모아 좀 더 친근하게 사용하고 관리할 수 있기를 바랍니다. 이것이 바로 Aspect 프로그래밍입니다. 측면 프로그래밍은 원격 코드 수정 방지를 기반으로 코드 재사용을 달성합니다. 서로 다른 사물을 수평으로 자르고 내부 방식의 변형에 집중하는 것과 같습니다. 객체 지향 프로그래밍은 전반적인 아키텍처 설계에 더 많은 관심을 기울입니다.
Class Person
{
    private int money
    public void pay(price)
    {
        try
        {
            if (checkAuthorize() == true) {
                this.money = this.money - price;   
                sendLog();
            }
        }
        catch (Exception e)
        {
 
        }  
    }
}

구현

  在上一节中介绍的duck punch与切面编程类似,都是在改造原方法的同时保证原方法功能。但就像结尾说的一样,直接修改原方法的模式有悖于面向对象最佳实践的原则。

  Javascript可以采用装饰者模式(给原对象添加额外的职责但避免修改原对象)实现AOP编程。注意在这里强调的是实现,我进一步想强调的是,切面编程只是一种思想,而装饰者模式只是实践这种思想的一种手段而已,比如在Java中又可以采用代理模式等。切面编程在Java中发挥的余地更多,也更标准,本想把Java的实现模式也搬来这篇文章中,但不才Java水平有限,对Java的实现不是非常理解。在这里就只展示Javascript的实现。

  AOP中有一些概念需要介绍一下,虽然我们不一定要严格执行

joint-point:原业务方法;

advice:拦截方式

point-cut:拦截方法

  关于这三个概念我们可以串起来可以这么理解:

  当我们使用AOP改造一个原业务方法(joint-point)时,比如加入日志发送功能(point-cut),我们要考虑在什么情况下(advice)发送日志,是在业务方法触发之前还是之后;还是在抛出异常的时候,还是由日志发送是否成功再决定是否执行业务方法。

  比如gihub上的meld这个开源项目,就是一个很典型的AOP类库,我们看看它的API:

// 假设我们有一个对象myObject, 并且该对象有一个doSomething方法:
 
var myObject = {
    doSomething: function(a, b) {
        return a + b;
    }
};
 
// 现在我们想拓展它,在执行那个方法之后打印出刚刚执行的结果:
 
var remover = meld.after(myObject, 'doSomething', function(result) {
    console.log('myObject.doSomething returned: ' + result);
});
 
// 试试执行看:
 
myObject.doSomething(1, 2); // Logs: "myObject.doSomething returned: 3"
 
// 这个时候我们想移除刚刚的修改:
 
remover.remove();

由此可以看出,AOP接口通常需要三个参数,被修改的对象,被修改对象的方法(joint-point),以及触发的时机(adivce),还有触发的动作(point-cut)。上面说了那么多的概念,现在可能要让各位失望了,Javascript的实现原理其实非常简单

function doAfter(target, method, afterFunc){
    var func = target[method];
    return function(){
        var res = func.apply(this, arguments);
        afterFunc.apply(this, arguments);
        return res;  
    };
}

当然,如果想看到更完备的解决方案和代码可以参考上面所说的meld项目

 结束语

  这一篇一定让你失望了,代码简单又寥寥无几。本篇主要在于介绍有关duck和AOP的这几类思想,我想编程的乐趣不仅仅在于落实在编码上,更在于整个架构的设计。提高代码的可维护性和可拓展性会比高深莫测的代码更重要。

  其实上面

 参考文献:

How to Fulfill Your Own Feature Request -or- Duck Punching With jQuery!

Duck Punching JavaScript - Metaprogramming with Prototype

Does JavaScript have the interface type (such as Java’s ‘interface’)?

AOP技术基础


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.