Magento中的架構(gòu)通常被認(rèn)為是過度設(shè)計(jì)了栗涂。如果從架構(gòu)的角度去看Magento的代碼,很容易發(fā)現(xiàn)它至少使用了下面介紹的的十二種設(shè)計(jì)模式祈争。
Magento its architecture is sometimes deemed overly engineered. If we look at it from a helicopter view, commonly used design patterns are easily spotted. Here are 12 of them.
介紹
給出一個(gè)需求斤程,解決的方案有很多,設(shè)計(jì)模式便是其中的最佳方法,可以解決特定環(huán)境下的同類問題忿墅。設(shè)計(jì)模式的代碼是可重復(fù)利用的扁藕,對于提高工作效率大有裨益,那從長遠(yuǎn)角度考慮疚脐,是不是應(yīng)當(dāng)在代碼中盡可能多的使用設(shè)計(jì)模式呢亿柑?明顯不是,一名優(yōu)秀的軟件開發(fā)者知道何時(shí)使用設(shè)計(jì)模式棍弄,并不會無的放矢望薄。早期Magento框架中的比較出色的一點(diǎn)是,其中的絕大部分(甚至所有)使用的設(shè)計(jì)模式的方式都有其用意所在(畫外音:說明Magento的開發(fā)者是一名優(yōu)秀的軟件開發(fā)者呼畸,不然本文也就沒有意義了)痕支。
本文是根據(jù)@Ryan Street的系列博文整理而得的。
Introduction
A software design pattern is a reusable solution to an often occurring problem. This doesn’t mean that software is better if it has more design patterns. Instead, a good software engineer should be able to spot the problem and implement the pattern instead of introducing implementations without purpose. The earlier behavior is leadingly noticeable in Magento, where most if not all design pattern-implementations have a purpose.
This is a compilation of an article-series which originally appeared on Ryan Street’s blog (@ryanstreet).
模式1:MVC
簡易程度:??
使用場景:?????
Model-View-Controller,即模型-視圖-控制器,簡稱MVC蛮原,應(yīng)該是最廣為人知的一種設(shè)計(jì)模式(大多數(shù)使用者甚至都不會將它視為設(shè)計(jì)模式)卧须。
這是一種將業(yè)務(wù)邏輯、頁面展示瞬痘、邏輯分離開來的設(shè)計(jì)模式故慈。Mageno中使用了大量的xml文件作為邏輯模板,使用pthml(混合了HTML和PHP)文件作為它的視圖框全,剩下的模型依賴Varien的ORM察绷。大多數(shù)的業(yè)務(wù)邏輯發(fā)生在模型中,而控制器將模型數(shù)據(jù)映射到視圖津辩,Magento的視圖包含了太多的邏輯而顯示很笨重拆撼,不得不通過一個(gè)專門的php類(Block類)進(jìn)行渲染。
Model View Controller Pattern
Model View Controller, MVC for short, is a design pattern where business, presentation and coupling logic are separated. Magento heavily utilizes XML as templating-logic and HTML mixed with PHP files for its views. Models are backed by ORM. Most business logic happens in the models whereas the controllers map the model-data to the views.
Because Magento its views are “fat” – they often contain a lot of logic – its not rare that views have an additional PHP class (the Block system) which will help with rendering
模式2: 前端控制器模式
簡易程度:??
使用場景:?????
前端控制器模式確保有且只有一個(gè)入口喘沿。所有的請求都會先從前端控制器那里走一遭闸度,被識別后路由分發(fā)到指定的controller,進(jìn)行特定的處理蚜印。
在Magento唯一一個(gè)入口文件index.php
就起到了前端控制器的作用莺禁,它通過Mage::app()
方法實(shí)現(xiàn)應(yīng)用環(huán)境的初始化并將請求路由到正確的controller中。
Front Controller Pattern
The front controller pattern makes sure that there is one and only one point of entry. All requests are investigated, routed to the designated controller and then processed accordingly to the specification. The front controller is responsible of initializing the environment and routing requests to designated controllers.
Magento has only one point of entry (index.php) which will initialize the application environment (Mage::app()) and route the request to the correct controller.
模式3:工廠模式
簡易程度:???
使用場景:?????
工廠模式的“工廠”二字已經(jīng)充分表露了它的功能————如工廠的流水線一般統(tǒng)一進(jìn)行類的實(shí)例化窄赋。
它被廣泛應(yīng)用在Magento的代碼庫中哟冬,負(fù)責(zé)自動加載系統(tǒng)。在config.xml
文件中定義一個(gè)module
的別名后忆绰,工廠就悄咪咪的記錄了別名對應(yīng)的類及類所在的位置浩峡。
Mage
核心類中有很多輔助實(shí)現(xiàn)工廠的方法,其中的Mage::getModel()
方法可以接收一個(gè)類的別名返回類的實(shí)例错敢,如Mage::getModel('catalog/product')
返回產(chǎn)品類Mage_Catalog_Model_Product
的實(shí)例翰灾。
區(qū)別于傳統(tǒng)的在代碼中直接引入類所在文件并調(diào)用類,工廠模式以統(tǒng)一的方式對類進(jìn)行初始化。
Factory Pattern
As implied by the name, the factory pattern is responsible of factorizing (instantiating) classes. It’s widely used through the Magento code base and leverages the autoloading system in Magento. By defining an alias in a module its config.xml you are letting the factory know where it can find classes.
There are various factory-helper methods in the Mage core class and one of them is getModel(). It accepts an alias for a class and will then return an instance of it. Instead of having include calls scattered through the code base, the factory pattern will instantiate classes in an uniform way.
模式4:單例模式
簡易程度:???
使用場景:?????
另一種獲取類的實(shí)例的方法是Mage::getSingleton()
纸淮,它跟Mage::getModel()
方法一樣接收一個(gè)類的別名平斩,不同之處在于getSingleton
在返回實(shí)例之前,會先去注冊表里瞄一眼咽块,看看這個(gè)類是否已經(jīng)實(shí)例化過了双戳,如果實(shí)例化過了,那這個(gè)實(shí)例就可以被共享了糜芳。
例如飒货,Magento中的session對象,(如customer session或checkout session),被儲存在注冊表中峭竣,可以在代碼不同地方重復(fù)使用塘辅,而不要每次都重新創(chuàng)建
Singleton Pattern
Another way to retrieve an instance of a class, is to call Mage::getSingleton(). It accepts a class alias and before returning an instance, it checks the internal registry whether this class has already been instantiated before – this results in a shared instance. An example of where this is mandatory, is the session storage which should be shared through the code base instead of creating it anew every time.
模式5:注冊模式
簡易程度:???
使用場景:????
(進(jìn)程級別的)
所有的單例都存儲在內(nèi)部注冊表中,這是全局的存儲數(shù)據(jù)的地方,而且不僅限于內(nèi)部使用。下面列舉的注冊相關(guān)的方法可以分別實(shí)現(xiàn)從注冊表中存儲皆撩,查詢扣墩,刪除數(shù)據(jù)。
Mage::register($key,$value) //存儲
Mage::registry() //查詢
Mage::unregister() //刪除
這種注冊表的方法通常應(yīng)用于數(shù)據(jù)不能傳遞時(shí)的場景下扛吞,進(jìn)行數(shù)據(jù)的傳輸呻惕。并且數(shù)據(jù)格式是key-value
的格式
比如訂單生成的時(shí)候register
一個(gè)key,然后在sales_order_save_after
事件的observer
方法中通過registry
讀取之前register
的key
Registry Pattern
All the singletons are stored in the internal registry: a global scoped Container for storing data. It is not only for internal use. The Mage::register($key, $value),::registry($key) and ::unregister($key) methods can be respectively used for storing, retrieving and removing data from the registry. The registry is often used for transferring data between scopes when they cannot be passed on, otherwise.
模式6:原型模式
簡易程度:????
使用場景:???
原型模式是對工廠模式功能的補(bǔ)充,它定義類的實(shí)例可以根據(jù)其父類(原型)檢索其它類的實(shí)例滥比。
舉個(gè)栗子亚脆,Mage_Catalog_Model_Product
類有一個(gè)getTypeInstance
方法來獲取特定的類Mage_Catalog_Model_Product_Type
的對象,后者包含了不適用于其它產(chǎn)品的一系列的方法和屬性盲泛。
而Mage_Downloadable_Model_Product_Type
這個(gè)Downloadable產(chǎn)品的類又最終繼承了Mage_Catalog_Model_Product_Type
類濒持。如果您正在下單并想要調(diào)用Downloadable類型產(chǎn)品的特定方法,則需要首先使用原始的產(chǎn)品類的getTypeInstance方法對其進(jìn)行實(shí)例化寺滚。
Prototype Pattern
Where the factory pattern (#3 on our list) stops, is where the prototype pattern continues. It defines that instances of classes can retrieve a specific other class instance depending on its parent class (the prototype). A notable example is the Mage_Catalog_Model_Product class which has a getTypeInstance method to retrieve the specificMage_Catalog_Model_Product_Type with a specific subset of methods and properties not applicable to all products.
For example, the Mage_Downloadable_Model_Product_Type ultimately extends the Mage_Catalog_Model_Product_Type. If you are iterating over an order and want to call a specific method of a downloadable product, you will need to factorize it first with the getTypeInstance method of the original product.
模式7:對象池模式
簡易程度:???
使用場景:???
對象池模式只是一個(gè)包含對象的集合柑营,防止它們一次又一次被分配和銷毀。
在Magento中村视,對象池模式并不常見官套,只會在處理嚴(yán)重影響服務(wù)器性能的重任務(wù)時(shí)被使用,例如批量導(dǎo)入產(chǎn)品的時(shí)候蚁孔。
可以使用Mage::objects()
方法訪問對象池(由Varien_Object_Cache
類管理)
Object Pool Pattern
The object pool pattern is simply a box with objects so that they do not have to be allocated and destroyed over and over again. It’s not used a lot in Magento other than for heavy tasks where resources can get limited soon, like importing products. The object pool (managed by Varien_Object_Cache) can be accessed with Mage::objects().
模式8 迭代器模式
簡易程度:????
使用場景:??
迭代器模式定義了一個(gè)公共方法來遍歷具有對象的結(jié)合奶赔。
在Magento中,這是由Varien_Data_Collection
類實(shí)現(xiàn)的勒虾,它依次使用各種baked-in的PHP類(如ArrayIterator
)來為數(shù)組提供更多的OO接口纺阔。這樣可以確保模型集合始終具有一個(gè)通用的API來遍歷瘸彤,而不依賴于實(shí)際的模型修然。
Iterator Pattern
The iterator pattern defines that there is a shared way to iterate over a container with objects. In Magento, this is handled by the Varien_Data_Collection which on its turn uses various baked-in PHP classes (like ArrayIterator) for having a more OO-interface to arrays. This ensures that model-collections will always have a common API to iterate over without being dependent of the actual models.
模式9:延遲加載模式
簡易程度:??
使用場景:????
延遲加載確保加載數(shù)據(jù)被延遲到實(shí)際需要的時(shí)間點(diǎn),這導(dǎo)致更少的資源利用。 Magento的延遲加載行為之一就是collection集合。如果使用Mage::getModel('catalog/product')->getCollection()
獲取產(chǎn)品collection時(shí)愕宋,并沒有操作數(shù)據(jù)庫玻靡。只有當(dāng)load之后,遍歷collection中的product對象或者查詢collection的數(shù)量時(shí)中贝,才會對數(shù)據(jù)庫進(jìn)行讀寫操作囤捻。
Lazy Loading Pattern
Lazy loading ensures that loading data is delayed until the point when it is actually needed. This results in less resources being used. One of the lazy loading behaviors of Magento is that of collections. If you were to retrieve a collection of products with Mage::getModel('catalog/product')->getCollection(), the database will only be touched when you actually access the collection by, for example, iterating over it or retrieving the count of models found.
模式10:服務(wù)定位器模式
簡易程度:????
使用場景:???
服務(wù)定位器模式抽取某個(gè)服務(wù)的檢索。它遵守其抽象基礎(chǔ)邻寿,可以在不破壞任何東西的情況下改變服務(wù)蝎土,而且可以看到適合其目的的服務(wù)。
例如绣否,Ryan的數(shù)據(jù)庫連接誊涯。
另一個(gè)例子是Magento的緩存機(jī)制,通過Mage::getCache()
存儲緩存蒜撮,Mage::getCache()
是由Zend或其他供應(yīng)商提供的緩存存儲的代理服務(wù)定位器暴构。
再如,隊(duì)列的實(shí)現(xiàn)
Service Locator Pattern
The service locator pattern abstracts away the retrieval of a certain service. This allows for changing the service without breaking anything (as it adheres to its abstract foundation) but also fetching the service as seen fit for its purpose.
Ryan exemplifies this with database connections. Another example is that of Magento its caching mechanism where Mage::getCache() is a service locator by-proxy for the cache storage supplied by Zend or other vendors.
模式11:模塊模式
簡易程度:???
使用場景:????
任何熟悉Magento開發(fā)的人都會很自然的接觸模塊模式段磨。它基本上定義了不同的功能被分組成獨(dú)立的模塊取逾,它們彼此獨(dú)立,并且可以根據(jù)需要插入到Magento主系統(tǒng)中苹支。在理想情況下砾隅,模塊模式的實(shí)現(xiàn)將確保每個(gè)元素都可以被刪除或交換。 PHP中模塊模式的主角之一是Composer軟件包管理器债蜜。
雖然Magento嚴(yán)重依賴于模塊化架構(gòu)琉用,但它并不是模塊化的。某些功能與核心密切相關(guān)策幼,不能輕易改變邑时。還大量使用超全局的Mage
核心類,引入了各種不受監(jiān)管的系統(tǒng)級依賴關(guān)系特姐。
Module Pattern
Anyone familiar with Magento development has stumbled upon the module pattern. It basically defines that different domains are grouped into separate modules which function independent of each other and can be plugged-in to the main system as deemed appropriate. In an ideal situation, an implementation of the module pattern would make sure that each element can be removed or swapped. One of the protagonists of the module pattern in PHP is the Composer package manager.
Though Magento heavily relies on a modular architecture, its not modular to the bone. Certain functionality is heavily tied to the core and can not be easily changed. There is also the heavy usage of the super-global Mage core-class which introduces all sorts of system-wide dependencies not easily overseen.
模式12:觀察者模式
簡易程度:??
使用場景:?????
Magento的事件驅(qū)動架構(gòu)是實(shí)現(xiàn)觀察者模式的結(jié)果晶丘。通過定義觀察者(或監(jiān)聽者),可以掛接額外的代碼唐含,隨著觀察到的事件觸發(fā)浅浮,這些代碼將被調(diào)用。
Magento使用config.xml
存儲并定義觀察者捷枯。使用Mage::dispatchEvent($eventName滚秩,$data)
觸發(fā)事件,eventName是事件名淮捆,data是參數(shù)郁油。事件觸發(fā)后會將查詢數(shù)據(jù)存儲本股,并觸發(fā)相應(yīng)的$event
觀察者。
除了使用模塊之外桐腌,還可以使用事件來定制現(xiàn)有的邏輯拄显,而不用接觸現(xiàn)有的代碼。
舉個(gè)栗子案站,下單完成后發(fā)送郵件通知用戶躬审,不需要將寫郵件的代碼跟下單的代碼寫在一起,只要下單完成后觸發(fā)事件sales_order_save_after
,在config.xml
中定義sales_order_save_after
事件的觀察者蟆盐,然后在觀察者方法中寫相應(yīng)的發(fā)送郵件的代碼
Observer Pattern
Magento its event-driven architecture is a result of an implementation of the observer pattern. By defining observers (or listeners), extra code can be hooked which will be called upon as the observed event fires. Magento uses its XML-data storage to define observers. If an event is fired with Mage::dispatchEvent($eventName, $data), the data storage will be consulted and the appropriate observers for $event will be fired.
In addition to using modules, events can be used to customize existing logic without touching the existing code.