观察者模式在Web开发中的应用

在面向对象编程中,设计模式是一套被广泛认可的解决特定问题的模板。观察者模式是其中之一,它帮助解耦对象,并确保遵循面向对象的设计原则,例如对象应该对修改封闭,对扩展开放。

观察者模式定义了对象之间的一对多依赖关系,使得当一个对象状态发生变化时,所有依赖它的对象都会自动收到通知并更新。这种依赖关系是在运行时建立的;在设计时,对象之间并不相互了解,是解耦的。称源对象为“可观察的”,依赖它的对象为“观察者”。

模式解释

让通过一个简单的AJAX应用程序来了解观察者模式。这个演示应用程序是一个简单的PHP代码,但设计概念并不局限于一种语言。这个小型应用程序的目的是,每当服务器端的温度值发生变化时,更新网页上的温度显示。工作流程很简单;网页每两秒发起一次AJAX请求,并通过回调函数获取更新的温度值。

服务器以摄氏度的温度值响应,网页显示它。假设应用程序运行良好,客户也很满意。第二天,客户想要增加一个选项,让客户可以选择以华氏度显示温度。这是可选的,客户可能会决定打开或关闭。该怎么办?应该修改回调函数,还是编写一个单独的函数来将摄氏度转换为华氏度,并为显示逻辑调用这些函数?无论哪种方式,父函数都需要在设计时知道要调用哪些函数。

function ajaxcallback(temperature){ myCelsiusDisplay(temperature); myFahrenheitDisplay(temperature); } function myCelsiusDisplay(temperature){ displayLogic } function myFahrenheitDisplay(temperature){ call conversion function to convert celsius to fahrenheit display logic }

在上述代码中,AJAX回调与其它显示函数高度耦合。因此,每次想要添加显示逻辑时,都需要编辑AJAX回调函数,这将不会是可重用的,因为它需要在设计时知道其他函数。它不能移动到另一个页面,其中显示逻辑可能会有所不同。此外,现在回调可能需要知道一个隐藏字段,比如复选框是否被选中。在更大的应用程序中,这种情况可能会变得更加复杂。

客户经常改变主意。可能会想知道,当他第二天要求以开尔文显示温度时,会怎么做。需要找到一个更好的解决方案。AJAX回调只知道在运行时调用的函数,而不是在设计时。这样,它就可以在任何页面上解耦并重用。这听起来是个好主意。称AJAX回调为“可观察的”,要调用的函数为“观察者”。可观察的将在发生某些变化时通知观察者。

使用代码

当点击摄氏度复选框时,它会以摄氏度显示温度,当点击华氏度复选框时,它会以华氏度显示温度。可以使显示更加花哨,比如温度计,并以图形形式显示数字,比如水银水平。

PHP

// Observable object which is observed by observers observable = new Object(); observable.observers = new Array(); observable.notify = function(message){ for(i=0; i

以上是可观察的JavaScript对象,它有一个'notify'函数。AJAX请求回调将温度值传递给所有观察者。它有'observers'数组,这是观察者将在运行时注册或移除的地方。

PHP

function addCelsiusDisplay(checkbox){ if(checkbox.checked == true){ observer1 = new Object(); observer1.Name = "celsiusdisplay"; observer1.doWork = function(information){ document.getElementById("celsius_display").value = information; } // register the observer observable.addObservers(observer1); }else{ // remove the observer observable.removeObservers("celsiusdisplay"); } }

这个函数在复选框点击事件上运行。如果复选框被选中,则将其添加到可观察对象的观察者列表中。如果未选中,则从观察者列表中移除观察者。所有这些耦合只在运行时发生;对象在设计时不知道彼此。

同样,可以在运行时添加任意数量的观察者,并以不同的方式显示温度值。不再需要更改可观察逻辑。任何对可观察的更改都是关键的,因为这种关系遵循一对多;更改可观察的可能会导致所有观察者发生变化。现在是安全的;不会更改可观察的。下面的代码将在不更改任何内容的情况下更新华氏度显示。

function addFarenheitDisplay(checkbox){ if(checkbox.checked == true){ observer1 = new Object(); observer1.Name = "farenheitdisplay"; observer1.doWork = function(information){ var farenheit = information * (9/5) + 32; document.getElementById("farenheit_display").value = farenheit; } observable.addObservers(observer1); }else{ observable.removeObservers("farenheitdisplay"); } }

在这里,可观察的不知道它处理的是什么数据。它只是传递服务器响应。所以这个模型可以与任何Web应用程序一起重用。这是个好消息。

下面的代码是AJAX请求,它异步地请求服务器端;页面不会刷新,可观察对象会保留。当响应到达时,它会调用可观察的函数。

function getData(){ var ajaxRequest = createXMLHttp(); ajaxRequest.open("GET", "temperature.php?unique_request=" + Math.random(), true); ajaxRequest.onreadystatechange = function(){ if(ajaxRequest.readyState == 4 && ajaxRequest.status == 200){ observable.notify(ajaxRequest.responseText); } } ajaxRequest.send(null); }

下面的代码每两秒执行一次AJAX请求,在JavaScript的多线程模式下与服务器通信。

var t = setInterval(getData, 2000);

PHP

if(file_exists('data.xml')) { $xml = simplexml_load_file('data.xml'); } else { exit('Failed to open data.'); } $data = $xml->temperature; $xml = null; echo $data;

服务器端做简单的工作。它读取一个XML文件并回显温度。

另一个服务器端代码用于更新这个温度在XML中:

if(isset($_REQUEST["action_id"]) && $_REQUEST["action_id"] != ""){ $data = $_REQUEST["temperature"]; $sxe = new SimpleXMLElement('data.xml', NULL, TRUE); $sxe->temperature = $data; file_put_contents('data.xml', $sxe->asXML()); echo "Temperature updated successfully"; }

测试

现在是测试所做的事情的时候了。解压缩附带的源代码。将其复制到Web服务器的根文件夹。使用服务器的正确URL在浏览器中运行observer.php。检查那些复选框;温度值将显示在文本框中。现在在另一个浏览器标签中运行dataeditor.php。更新温度值。查看显示页面(observer.php),它现在会更新。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485