在之前的兩篇文章中,我們看到了一些在Spring框架中實(shí)現(xiàn)的設(shè)計(jì)模式。這一次我們會(huì)發(fā)現(xiàn)這個(gè)流行框架使用的3種新模式。本文將從描述兩個(gè)創(chuàng)意設(shè)計(jì)模式開(kāi)始:原型和對(duì)象池。最后我們將重點(diǎn)關(guān)注行為模式—>觀察者。
原型模式
這篇文章的第一個(gè)設(shè)計(jì)模式是原型。可以通過(guò)官方文檔查找有關(guān)Spring作用域中的bean作用域的文章中介紹了類(lèi)似的概念(prototype)。原型設(shè)計(jì)模式與有用相同名稱(chēng)的(prototype)作用域有點(diǎn)相似。此設(shè)計(jì)模式允許通過(guò)復(fù)制已存在的對(duì)象來(lái)創(chuàng)建一個(gè)對(duì)象的實(shí)例。副本應(yīng)該是真正的副本。這意味著新對(duì)象的所有屬性應(yīng)與復(fù)制對(duì)象的屬性相同。如果不清楚,比一個(gè)簡(jiǎn)單的JUnit案例更好的說(shuō)明:
public class PrototypeTest {
@Test
public void test() {
Robot firstRobot = new Robot("Droid#1");
Robot secondRobot = (Robot) firstRobot.clone();
assertTrue("Cloned robot's instance can't be the same as the"
+" source robot instance",
firstRobot != secondRobot);
assertTrue("Cloned robot's name should be '"+firstRobot.getName()+"'"
+" but was '"+secondRobot.getName()+"'",
secondRobot.getName().equals(firstRobot.getName()));
}
}
class Robot implements Cloneable {
private String name;
public Robot(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
在Spring中,在org.springframework.beans.factory.support.AbstractBeanFactory中使用一種特定的原型設(shè)計(jì)模式,它將初始化bean原型作用域。新對(duì)象基于配置文件中的bean定義。我們可以看到,在給定的例子中:
<bean id="shoppingCart" class="com.waitingforcode.data.ShoppingCart" scope="prototype">
<property name="id" value="9"></property>
</bean>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"applicationContext-test.xml"})
public class SpringPrototypeTest {
@Autowired
private BeanFactory beanFactory;
@Test
public void test() {
ShoppingCart cart1 = (ShoppingCart) beanFactory.getBean("shoppingCart");
assertTrue("Id of cart1 should be 9 but was "+cart1.getId(),
cart1.getId() == 9);
cart1.setId(100);
ShoppingCart cart2 = (ShoppingCart) beanFactory.getBean("shoppingCart");
assertTrue("Id of cart2 should be 9 but was "+cart2.getId(),
cart2.getId() == 9);
assertTrue("Id of second cart ("+cart2.getId()+") shouldn't be the same as the first one: "+cart1.getId(),
cart1.getId() != cart2.getId());
cart2.setId(cart1.getId());
assertTrue("Now (after cart2.setId(cart1.getId())), the id of second cart ("+cart2.getId()+") should be the same as the first one: "
+cart1.getId(), cart1.getId() == cart2.getId());
assertTrue("Both instance shouldn't be the same", cart1 != cart2);
}
}
從前面的例子可以看出,ShoppingCart實(shí)例是直接從bean定義創(chuàng)建的。最初,cart1和cart2對(duì)象的id值為9.它在測(cè)試結(jié)束時(shí)被修改,以證明兩個(gè)引用都屬于兩個(gè)不同的對(duì)象。
對(duì)象池
Spring中使用的另一個(gè)模型是對(duì)象池設(shè)計(jì)模式。其主要目的在于在一個(gè)池中保存特定數(shù)量的對(duì)象,并根據(jù)需要重新使用。通過(guò)它,我們可以改善我們想要使用巨型對(duì)象的響應(yīng)時(shí)間。巨型意味著這些對(duì)象的構(gòu)造需要很多時(shí)間(例如:持有數(shù)據(jù)庫(kù)連接的對(duì)象),最好重用已經(jīng)存在的和未獲取的對(duì)象,而不是創(chuàng)建新對(duì)象。
Spring還使用線(xiàn)程池來(lái)管理其調(diào)度部分。一些示例位于org.springframework.scheduling.concurrent中。我們檢索數(shù)據(jù)庫(kù)(Spring JDBC)項(xiàng)目中的對(duì)象池的想法。數(shù)據(jù)庫(kù)連接池不是由Spring直接實(shí)現(xiàn)的,而是適用于Spring工作方式的項(xiàng)目,如C3P0或Jakarta Commons DBCP連接池。
觀察者
這里呈現(xiàn)的最后一個(gè)設(shè)計(jì)模式是觀察者。當(dāng)一個(gè)或幾個(gè)課程正在等待具體事件時(shí)可以使用它。觀察者模式由一個(gè)科目和觀察員名單組成。一個(gè)很好的例子就是GUI界面,其中點(diǎn)擊按鈕(按鈕是主題)會(huì)引起聽(tīng)眾(觀察者)啟動(dòng)的一些操作(再說(shuō)的直白點(diǎn)就是電影院一場(chǎng)電影這個(gè)subject,需要觀眾(也就是觀察者咯),電影產(chǎn)生的一些畫(huà)面產(chǎn)生的事件,比如恐怖 電影給男人女人帶來(lái)的不同的感官的感受,傳播到觀察者也就是觀眾的眼里所帶來(lái)的不一樣的反應(yīng),這個(gè)中間一般會(huì)添加一個(gè)事件傳播者,在后面解釋Spring的例子的時(shí)候會(huì)說(shuō)到),例如:打開(kāi)一個(gè)新頁(yè)面這個(gè)動(dòng)作。可以參考下面的例子:
public class ObserverTest {
@Test
public void test() {
Observer pageOpener = new PageOpener();
Observer register = new Register();
Button btn = new Button();
btn.addListener(pageOpener);
btn.addListener(register);
btn.clickOn();
assertTrue("Button should be clicked but it wasn't",
btn.wasClicked());
assertTrue("Page opener should be informed about click but it wasn't",
pageOpener.wasInformed());
assertTrue("Register should be informed about click but it wasn't",
register.wasInformed());
}
}
class Button {
private boolean clicked;
private List<observer> listeners;
public List<observer> getListeners() {
if (this.listeners == null) {
this.listeners = new ArrayList<observer>();
}
return this.listeners;
}
public void addListener(Observer observer) {
getListeners().add(observer);
}
public boolean wasClicked() {
return this.clicked;
}
public void clickOn() {
this.clicked = true;
informAll();
}
private void informAll() {
for (Observer observer : getListeners()) {
observer.informAboutEvent();
}
}
}
abstract class Observer {
protected boolean informed;
public void informAboutEvent() {
this.informed = true;
}
public boolean wasInformed() {
return this.informed;
}
}
class PageOpener extends Observer {
@Override
public void informAboutEvent() {
System.out.println("Preparing download of new page");
super.informAboutEvent();
}
}
class Register extends Observer {
@Override
public void informAboutEvent() {
System.out.println("Adding the action to register");
super.informAboutEvent();
}
}
可以看到,關(guān)于我們的Button實(shí)例點(diǎn)擊的事件被發(fā)送到所有的觀察者對(duì)象。從這些對(duì)象開(kāi)始下載頁(yè)面內(nèi)容,第二個(gè)將在事件的信息保存在注冊(cè)表中。在Spring中,觀察者設(shè)計(jì)模式用于將與應(yīng)用程序上下文相關(guān)的事件傳輸?shù)給rg.springframework.context.ApplicationListener的實(shí)現(xiàn)。要了解它們的實(shí)現(xiàn)方法,我們來(lái)看一下AbstractApplicationContext類(lèi)(老版本的代碼,新版本的請(qǐng)自行對(duì)照):
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext, DisposableBean {
/** Statically specified listeners */
private Set<applicationlistener<?>> applicationListeners = new LinkedHashSet<applicationlistener<?>>();
// some other fields and methods
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
if (this.applicationEventMulticaster != null) {
this.applicationEventMulticaster.addApplicationListener(listener);
}
else {//新版本這里直接咔嚓掉,上面的applicationEventMulticaster一旦為空,就會(huì)報(bào)錯(cuò)的
this.applicationListeners.add(listener);
}
}
/**
* Return the list of statically specified ApplicationListeners.
*/
public Collection<applicationlistener<?>> getApplicationListeners() {
return this.applicationListeners;
}
/**
* Add beans that implement ApplicationListener as listeners.
* Doesn't affect other listeners, which can be added without being beans.
*/
protected void registerListeners() {
// Register statically specified listeners first.
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String lisName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(lisName);
}
}
}
在提供的代碼中,監(jiān)聽(tīng)器在內(nèi)部添加到應(yīng)用程序上下文類(lèi)中,并且在registerListeners()方法之后,它們被注冊(cè)到由接口org.springframework.context.event.ApplicationEventMulticaster表示的適當(dāng)?shù)氖录嗦窂V播器(因?yàn)橛泻芏鄉(xiāng)isteners)。EventMulticaster負(fù)責(zé)管理不同的listener和向他們發(fā)布事件。
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
private Executor taskExecutor;
private ErrorHandler errorHandler;
public SimpleApplicationEventMulticaster() {
}
public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {
this.setBeanFactory(beanFactory);
}
public void setTaskExecutor(Executor taskExecutor) {
this.taskExecutor = taskExecutor;
}
protected Executor getTaskExecutor() {
return this.taskExecutor;
}
public void setErrorHandler(ErrorHandler errorHandler) {
this.errorHandler = errorHandler;
}
protected ErrorHandler getErrorHandler() {
return this.errorHandler;
}
public void multicastEvent(ApplicationEvent event) {
this.multicastEvent(event, this.resolveDefaultEventType(event));
}
//發(fā)布事件:通過(guò)池執(zhí)行任務(wù)的方式來(lái)做并發(fā)處理,這樣就把之前的對(duì)象池模式給利用上了
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
ResolvableType type = eventType != null?eventType:this.resolveDefaultEventType(event);
Iterator var4 = this.getApplicationListeners(event, type).iterator();
while(var4.hasNext()) {
final ApplicationListener<?> listener = (ApplicationListener)var4.next();
Executor executor = this.getTaskExecutor();
if(executor != null) {
executor.execute(new Runnable() {
public void run() {
SimpleApplicationEventMulticaster.this.invokeListener(listener, event);
}
});
} else {
this.invokeListener(listener, event);
}
}
}
...
}
這次我們講3種設(shè)計(jì)模式:用于在同一個(gè)調(diào)用作用域內(nèi)創(chuàng)建bean的原型,避免重新創(chuàng)建巨型對(duì)象的對(duì)象池,以及將應(yīng)用程序的上下文事件分派給適當(dāng)?shù)谋O(jiān)聽(tīng)器的觀察者。
原文:Spring框架中的設(shè)計(jì)模式(三)
轉(zhuǎn)載:極樂(lè)科技