Heim >Web-Frontend >js-Tutorial >AOP-Programmierung in Javascript
Duck Punch
Reden wir nicht zuerst über die AOP-Programmierung, sondern beginnen wir mit der Duck Punch-Programmierung.
Wenn Sie in Wikipedia nach Duck Punch suchen, sollten Sie den Eintrag Monkey Patch finden. Der Erklärung zufolge stammt das Wort Monkey Patch von Guerilla Patch, was bedeutet, dass der Code während des Betriebs stillschweigend geändert wird. Das Wort Guerilla hat die gleiche Aussprache wie Gorilla, und letzteres bedeutet ähnlich wie Affe (ersteres bedeutet „Gorilla“) entwickelte sich schließlich zu For Monkey Patch.
Wenn Sie noch nie von Duck Punch gehört haben, haben Sie vielleicht schon von Duck Typing gehört. Um ein beliebtes Beispiel zu nennen, wie man eine Ente identifiziert:
Wenn ich einen Vogel sehe, der wie eine Ente geht und schwimmt wie eine Ente und quakt wie eine Ente, nenne ich diesen Vogel eine Ente.
Ja, wenn ich ein Tier finde, das quakt wie eine Ente und schwimmt wie eine Ente, dann wäre es eine Ente!
Dieser Test mag ein wenig offensichtlich und unsinnig erscheinen, aber er ist sehr praktisch. Und es kann verwendet werden, um eine Art Problem in der Programmierung zu lösen – wie implementiert man für Javascript oder ähnliche dynamische Sprachen „Schnittstelle“ oder „Basisklasse“? Wir müssen uns überhaupt nicht um ihre Vergangenheit kümmern, wenn wir sie verwenden:
var quack = someObject.quack; if (typeof quack == "function" && quck.length == arguLength) { // This thing can quack }
Ich gehe tatsächlich zu weit. Was ich zum Ausdruck bringen möchte, ist, dass sich Duck Punch tatsächlich aus dem Ententippen entwickelt hat:
Wenn es wie eine Ente läuft und wie eine Ente spricht, ist es eine Ente, oder? Wenn du willst, musst du die Ente einfach so lange schlagen, bis sie das zurückgibt, was du erwartest. Ein sehr anschaulicher Witz kommt mir in den Sinn:
Um die Stärke der Polizei in den Vereinigten Staaten, Hongkong und anderen zu testen Auf dem chinesischen Festland platzierten die Vereinten Nationen drei Kaninchen in drei Wäldern, um zu sehen, wer von den drei Polizisten das Kaninchen zuerst finden konnte. Aufgabe: Finde das Kaninchen. (Die Mitte entfällt...) Schließlich waren es nur vier Polizisten aus einem bestimmten Land. Sie spielten einen Tag lang Mahjong, jeder von ihnen nahm einen Schlagstock und betrat innerhalb von fünf Minuten die Schreie von Tieren, die aus dem Wald kamen, redete und lachte, während er eine Zigarette rauchte, und hinter ihm war ein Bär mit einer verletzten Nase und einem geschwollenen Gesicht und sagte: „Kämpfe nicht mehr, ich bin nur ein.“ Kaninchen..."
Entenpunsch ist zwar etwas heftig, aber es ist eine wirksame Methode. Bei der Code-Implementierung geht es darum, den Originalcode mit den von uns benötigten Funktionen kompatibel zu machen. Zum Beispiel dieses Beispiel auf dem Blog von Paul Irish:
Gleichzeitig kann der Entenschlagmodus aufgehoben werden, aber es ist einfach so:/** 我们都知道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')Aber da Hier liegt ein Problem vor: Die ursprüngliche Methode muss geändert werden. Dies verstößt gegen das „Offen-Geschlossen“-Prinzip, das für Erweiterungen offen und für Änderungen geschlossen sein sollte. Wie kann dieses Problem gelöst werden? Verwenden Sie die AOP-Programmierung.
(function($){ var _old = $.fn.method; $.fn.method = function(arg1,arg2){ if ( ... condition ... ) { return .... } else { // do the default return _old.apply(this,arguments); } }; })(jQuery);
AOP
Erste Schritte
AOP steht für Aspektorientierte Programmierung, was offensichtlich mit der objektorientierten Programmierung verwandt ist. Aspekt kann als „Aspekt“ oder „Seite“ übersetzt werden, AOP ist also aspektorientierte Programmierung.
Wie versteht man Aspekte?
In der objektorientierten Programmierung sind die von uns definierten Klassen normalerweise Domänenmodelle, und die Methoden, über die sie verfügen, beziehen sich normalerweise auf reine Geschäftslogik. Zum Beispiel:
Aber normalerweise ist die tatsächliche Situation komplizierter. Beispielsweise müssen wir der Zahlungsmethode eine Autorisierungserkennung hinzufügen oder Protokolle für Statistiken oder sogar fehlertoleranten Code senden. Der Code sieht also so aus:Class Person { private int money; public void pay(int price) { this.money = this.money - price; } }Noch beängstigender ist, dass ähnlicher Code zu anderen Methoden hinzugefügt werden muss. Auf diese Weise sind die Wartbarkeit und Lesbarkeit des Codes zu einem großen Problem geworden. Wir hoffen, diese verstreuten, aber gemeinsamen, nicht geschäftlichen Codes zu sammeln und sie benutzerfreundlicher zu nutzen und zu verwalten. Dies ist Aspektprogrammierung. Durch die Aspektprogrammierung wird die Wiederverwendung von Code erreicht, indem eine Änderung des Remotecodes vermieden wird. Es ist, als würde man verschiedene Objekte horizontal schneiden und sich auf die Transformation interner Methoden konzentrieren. Bei der objektorientierten Programmierung wird dem gesamten Architekturdesign mehr Aufmerksamkeit geschenkt.
Class Person { private int money public void pay(price) { try { if (checkAuthorize() == true) { this.money = this.money - price; sendLog(); } } catch (Exception e) { } } }
Implementierung
在上一节中介绍的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技术基础