1 SRP
SRP(Single Responsible Principle), 單一職責(zé)原則,這是面對的最基本原則,也是實(shí)現(xiàn)彈性設(shè)計(jì)的最基本原則。
每個(gè)類或接口定義應(yīng)該只包含一種明確的職責(zé),同時(shí)僅有一種原因會(huì)導(dǎo)致這種定義的修改。一個(gè)復(fù)雜的類或接口的定義包含多個(gè)責(zé)任,很容易使你的設(shè)計(jì)失去彈性,很多因素都會(huì)導(dǎo)致這個(gè)類或接口的變更,由于它含有多種職責(zé),這就意味著它是多種服務(wù)的提供者,會(huì)有多種依賴于它的客戶類,他的變更可能會(huì)導(dǎo)致大范圍的變更。
在作者看來,優(yōu)先級(jí)最高的是你首先要保證接口的單一職責(zé)及方法的單一職責(zé),接口通常意味可以更換不同的實(shí)現(xiàn),為一個(gè)接口定義過多的職責(zé)意味著每個(gè)實(shí)現(xiàn)都會(huì)涉及多個(gè)職責(zé),這將導(dǎo)致無法實(shí)現(xiàn)更小粒度的實(shí)現(xiàn)的復(fù)用。
2 面向抽象編成
如果你已經(jīng)讀過GOF的《設(shè)計(jì)模式》,你便知道其中每一個(gè)模式都是基于此原則的,抽象(或接口)有效的解除了服務(wù)調(diào)用者和服務(wù)提供者間的耦合。
3 使用配置
通過修改配置文件便可以改變系統(tǒng)的某些特性,這種修改的區(qū)別于修改代碼,對于Java,C++而言這種修改是不需要編譯,有的修改甚至可以在運(yùn)行時(shí)生效。DSL地運(yùn)用可以使配置更加具有可讀性及更強(qiáng)的描述能力。在設(shè)計(jì)時(shí)將實(shí)現(xiàn)分為配置及框架部分是非常靈活的結(jié)構(gòu)。
蔡超
HP 軟件架構(gòu)師
軟件架構(gòu)顧問
SCEA
IBM Certified Solution Designer for OOA&D vUML2
Chaocai2001@yahoo.com.cn
厭倦了那些厚書(特別是那些為了賺錢而特意寫厚的書),很多時(shí)候這些書讓我們找不到技術(shù)要點(diǎn),甚至喪失了學(xué)習(xí)的興趣,而最終變成那些拒絕新技術(shù)的“頑固派”。
其實(shí)掌握技術(shù)的最佳方式是實(shí)踐,在實(shí)踐中不斷的深入學(xué)習(xí)。
本教程旨在幫助哪些已經(jīng)掌握了OSGi和Spring技術(shù)基礎(chǔ)的開發(fā)人員,迅速將Spring DM應(yīng)用于實(shí)際開發(fā),這是一份入門教程,不求全面,但求簡單。
并請配合本教程的實(shí)例代碼一同學(xué)習(xí)。
下載教程和示例:
http://www.tkk7.com/Files/chaocai/spring-osgi.rar
實(shí)現(xiàn)外部DSL
與上一篇中所提及內(nèi)部DSL不同,使用者不是通過API調(diào)用來使用DSL,而是通過我們定義的特定語法的領(lǐng)域語言來使用DSL。
1 XML形式的DSL
腳本文件
<process name="Auto-Door">
<state name="Open">
<transition event="time-out" next_state="Close"/>
</state>
<state name="Close">
<transition event="people-closer" next_state="Open"/>
</state>
</process>
實(shí)現(xiàn)
publicclass XmlConfigParser {
//followings are context variables
private Machine currentMachine;
private State currentState;
class ElementHandler extends DefaultHandler{
private String getAttributeValue(String elemName,String attributeName,Attributes attris){
String attrValue=attris.getValue(attributeName);
if (attrValue==null){
thrownew XmlConfigParseException("Element "+elemName+" shoudle have the attribute:"+attributeName);
}
return attrValue;
}
@Override
publicvoid endElement(String arg0, String arg1, String elemName)
throws SAXException {
if (elemName.equals("state")){
currentMachine.getStates().add(currentState);
}
}
@Override
publicvoid startElement(String arg0, String arg1, String elemName,
Attributes attris) throws SAXException{
if (elemName.equals("process")){
String processName=getAttributeValue(elemName,"name",attris);
currentMachine=new Machine(processName);
}
if (elemName.equals("state")){
String stateName=getAttributeValue(elemName,"name",attris);
currentState=new State(stateName);
}
if (elemName.equals("transition")){
String eventName=getAttributeValue(elemName,"event",attris);
String nextState=getAttributeValue(elemName,"next_state",attris);
Transition transition=new Transition();
transition.setEvent(new Event(eventName));
transition.setNextState(nextState);
currentState.getTransitions().add(transition);
}
}
}
public Machine parser(String fileName){
SAXParserFactory spfactory =
SAXParserFactory.newInstance();
try{
SAXParser saxParser =
spfactory.newSAXParser();
XMLReader reader=saxParser.getXMLReader();
reader.setContentHandler(new ElementHandler());
reader.parse(fileName);
returncurrentMachine;
}catch(Exception e){
thrownew XmlConfigParseException("parsing is failed",e);
}
}
}
實(shí)現(xiàn)要點(diǎn)
上述實(shí)現(xiàn)是通過SAX來進(jìn)行XML解析的。
1 將領(lǐng)域模型結(jié)構(gòu)直接映射為XML元素的結(jié)構(gòu)
我們用這種方式來設(shè)計(jì)我們的DSL,這樣做的好處是DSL比較容易使用(更接近領(lǐng)域模型),同時(shí)解析程序也會(huì)相對簡單,比較容易生成相應(yīng)的語義模型。
2 使用上下文變量
如上面程序中的:
private Machine currentMachine;
private State currentState;
他們就是上下文變量,由于SAX是順序解析的,所以必須保持正確的工作上下文,如把生產(chǎn)Transition對象加入到正確的State中。
2 自定義語言
腳本文件
Machine (Auto-Door){
State(Open){
Transition{
event : time-out ,
next-state : Close
}
}
State (Close){
Transition{
event : people-closer ,
next-state : Open
}
}
}
實(shí)現(xiàn)
自己設(shè)計(jì)語法并實(shí)現(xiàn)解析器,通常需要我們具備一定的編譯原理知識(shí)并且借用一定的解析器生成工具來幫助我們生產(chǎn)解析器代碼。
實(shí)現(xiàn)中本人使用了 Antlr
Antlr的語法描述文件:
grammar StateMachineG;
@header {
import org.ccsoft.statemachine.models.Machine;
import org.ccsoft.statemachine.models.State;
import org.ccsoft.statemachine.models.Transition;
import org.ccsoft.statemachine.models.Event;
}
@members {
public void emitErrorMessage(String msg) {
throw new RuntimeException(msg);
//super.emitErrorMessage(msg);
}
}
machine returns [Machine value] : 'Machine''('NAME')''{'{$value=new Machine($NAME.text);} (e=state{$value.getStates().add($e.value);})+'}';
state returns [State value] : 'State''('NAME')''{'{$value=new State($NAME.text);}(e=transition{$value.getTransitions().add($e.value);})+'}';
transition returns [Transition value]
: 'Transition''{'{$value=new Transition();}e=event{$value.setEvent($e.value);}','f=nextState{$value.setNextState($f.value);}'}';
event returns [Event value] : 'event'':'e=NAME{$value=new Event($NAME.text);};
nextState returns [String value]
: 'next-state'':'e=NAME{$value=$NAME.text;};
NAME : ('a'..'z' |'A'..'Z'|'0'..'9')+ ;
WS : (' ' |'"t' |'"n' |'"r' )+ {skip();} ;
實(shí)現(xiàn)要點(diǎn)
1 采用Antlr的內(nèi)嵌Action
對于DSL的通常應(yīng)用即通過外部腳本生產(chǎn)相關(guān)部分語義模型對象,使用Antlr的內(nèi)嵌Action比采用語法樹方式簡單得多。
摘要:
引言
DSL(domain-specific language)并不是什么新的概念和技術(shù),但是目前它已成為了一個(gè)技術(shù)熱點(diǎn),近期各種類型的技術(shù)交流或研討會(huì)上你都可以看到關(guān)于DSL的主題。DSL似乎也在一夜間成為了大師們關(guān)注的焦點(diǎn)(Martin Fowler,Eric Evans等等)。
應(yīng)用DSL可以有效的提高系統(tǒng)的可維護(hù)性(縮小了實(shí)現(xiàn)模型和領(lǐng)域模型的距離,提高了實(shí)現(xiàn)的可讀性)和...
閱讀全文
在很多大型應(yīng)用中都會(huì)對數(shù)據(jù)進(jìn)行切分,并且采用多個(gè)數(shù)據(jù)庫實(shí)例進(jìn)行管理,這樣可以有效提高系統(tǒng)的水平伸縮性。而這樣的方案就會(huì)不同于常見的單一數(shù)據(jù)實(shí)例的方案,這就要程序在運(yùn)行時(shí)根據(jù)當(dāng)時(shí)的請求及系統(tǒng)狀態(tài)來動(dòng)態(tài)的決定將數(shù)據(jù)存儲(chǔ)在哪個(gè)數(shù)據(jù)庫實(shí)例中,以及從哪個(gè)數(shù)據(jù)庫提取數(shù)據(jù)。
Figure 1 數(shù)據(jù)分割及多數(shù)據(jù)庫架構(gòu)
通常這種多數(shù)據(jù)源的邏輯會(huì)滲透到業(yè)務(wù)邏輯中,同時(shí)也會(huì)給我們使用的數(shù)據(jù)訪問API諸如Hibernate和iBatis等帶來不便(需要指定多個(gè)SessionFactory或SqlMapClient實(shí)例來對應(yīng)多個(gè)DataSource)。

Figure 2 多數(shù)據(jù)源的選擇邏輯滲透至客戶端
解決方案

Figure 3 采用Proxy模式來封裝數(shù)據(jù)源選擇邏輯
通過采用Proxy模式我們在方案中實(shí)現(xiàn)一個(gè)虛擬的數(shù)據(jù)源,并且用它來封裝數(shù)據(jù)源選擇邏輯,這樣就可以有效地將數(shù)據(jù)源選擇邏輯從Client中分離出來。
Client提供選擇所需的上下文(因?yàn)檫@是Client所知道的),由虛擬的DataSource根據(jù)Client提供的上下文來實(shí)現(xiàn)數(shù)據(jù)源的選擇。
Spring2.x的版本中提供了實(shí)現(xiàn)這種方式的基本框架,虛擬的DataSource僅需繼承AbstractRoutingDataSource實(shí)現(xiàn)determineCurrentLookupKey()在其中封裝數(shù)據(jù)源的選擇邏輯。
實(shí)例:
publicclass DynamicDataSource extends AbstractRoutingDataSource {
static Logger log = Logger.getLogger("DynamicDataSource");
@Override
protected Object determineCurrentLookupKey() {
String userId=(String)DbContextHolder.getContext();
Integer dataSourceId=getDataSourceIdByUserId(userId);
return dataSourceId;
}
}
實(shí)例中通過UserId來決定數(shù)據(jù)存放在哪個(gè)數(shù)據(jù)庫中。
配置文件示例:
<bean id="dataSource" class="com.bitfone.smartdm.datasource.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.Integer">
<entry key="0" value-ref="dataSource0"/>
<entry key="1" value-ref="dataSource1"/>
<entry key="2" value-ref="dataSource2"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="dataSource0"/>
</bean>
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation" value="classpath:com/bitfone/smartdm/dao/sqlmap/sql-map-config.xml"/>
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="UserInfoDAO" class="com.bitfone.smartdm.dao.impl.UserInfoDAO">
<property name="sqlMapClient" ref="sqlMapClient"/>
</bean>
蔡超
HP 軟件架構(gòu)師
軟件架構(gòu)顧問
SCEA
IBM Certified Solution Designer for OOA&D vUML2
Chaocai2001@yahoo.com.cn
Mini-Container 在已發(fā)布在SourceForge上
相關(guān)鏈接:http://www.tkk7.com/chaocai/archive/2008/05/26/203020.html
項(xiàng)目地址:
http://sourceforge.net/projects/mini-container
在OO中可以使用抽象方法及接口來完成文中通過函數(shù)指針和結(jié)構(gòu)體來實(shí)現(xiàn)的間接層。

Client.java
AppInterface app=new AppImpl();
AppInterface app1=new AppProxy(app);
AppProxy.java
public class AppProxy implements AppInterface{
private AppInterface appRef;
public AppProxy(AppInterface appRef){
this.appRef=appRef
}
public void doSomething(){
/*some codes*/
}
}
通過Proxy來實(shí)現(xiàn)間接層,相互嵌套可以實(shí)現(xiàn)多個(gè)間接層,并且可以通過一個(gè)AppBuilder來創(chuàng)建這個(gè)對象,組合多個(gè)間接層。間接層中可以實(shí)現(xiàn)文中提及的對參數(shù)的預(yù)處理。
同時(shí),我也認(rèn)為文中提及的間接層也可以是Adapter。
本文介紹了常見面向?qū)ο笳Z言(Java,C#等)OverLoad對于運(yùn)行時(shí)執(zhí)行的方法邦定的局限,并且如何通過Double Dispatch來實(shí)現(xiàn)運(yùn)行時(shí)行為邦定。
1 根據(jù)對象來選擇行為問題
public interface Event {
}
public class BlueEvent implements Event {
}
public class RedEvent implements Event {
}
public class Handler {
public void handle(Event event){
System.out.println("It is event");
}
public void handle(RedEvent event){
System.out.println("It is RedEvent");
}
public void handle(BlueEvent event){
System.out.println("It is BlueEvent");
}
}
public class Main {
public static void main(String[] args) {
Event evt=new BlueEvent();
new Handler().handle(evt);
}
}
你認(rèn)為運(yùn)行結(jié)果是什么呢?
結(jié)果:It is event
是不是有點(diǎn)出乎意料,不是It is BlueEvent,這是應(yīng)為Overload并不支持在運(yùn)行時(shí)根據(jù)參數(shù)的運(yùn)行時(shí)類型來幫定方法,所以要執(zhí)行哪個(gè)方法是在編譯時(shí)就選定了的。
2 Double Dispatch Pattern
由于Java,C++及C#都具有上述局限,通常我們只能通過Switch或if結(jié)構(gòu)來實(shí)現(xiàn),當(dāng)然這種實(shí)現(xiàn)方式既不優(yōu)雅而且影響代碼的可維護(hù)性。
通過以下的Double Dispatch Pattern便可以優(yōu)雅的實(shí)現(xiàn)。
public interface Event {
public void injectHandler(EventHandler v);
}
public class BlueEvent implements Event {
public void injectHandler(EventHandler v) {
v.handle(this);
}
}
public class RedEvent implements Event {
public void injectHandler(EventHandler v) {
v.handle(this);
}
}
public class EventHandler {
public void handle(BlueEvent e){
System.out.println("It is BlueEvent");
}
public void handle(RedEvent e){
System.out.println("It is RedEvent");
}
}
public class Main {
public static void main(String[] args) {
Event evt=new BlueEvent();
evt.injectHandler(new EventHandler());
}
}
其實(shí)設(shè)計(jì)模式(GoF)中的Visitor模式就是Double Dispatch的一種應(yīng)用。
蔡超
HP
軟件架構(gòu)師
軟件架構(gòu)顧問
SCEA,SCBCD,MCSD
IBM Certified Solution Designer for
OOA&D vUML2
Chaocai2001@yahoo.com.cn,chao.cai@hp.com
Spring DM 1.1.x的最大特性便是它可以支持在其中部署WEB應(yīng)用,我使用后感覺這是個(gè)很酷的特性,我甚至覺得用這種方式開發(fā)基于OSGi WEB應(yīng)用比使用Spring DM Server更好,至少目前你可以獲得更好的便攜性(可以在多個(gè)Spring DM支持的OSGi平臺(tái)上運(yùn)行),并且Spring DM Server并沒有提供更多的企業(yè)應(yīng)用支持。
不過對于剛使用Spring DM進(jìn)行WEB應(yīng)用開發(fā)的人來說,成功地配置卻不是一件容易的事。以下詳細(xì)的講解一下相關(guān)配置。
1 運(yùn)行環(huán)境所需的Bundles:
0 ACTIVE system.bundle_3.2.2.R32x_v20070118
1 ACTIVE com.springsource.slf4j.api_1.5.0
2 RESOLVED org.springframework.osgi.jetty.web.extender.fragment.osgi_1.0.0
Master=46
3 ACTIVE org.springframework.bundle.osgi.extender_1.0.1.v200803070100
4 ACTIVE org.springframework.bundle.spring.core_2.5.5
5 ACTIVE org.springframework.bundle.spring.web_2.5.5
6 ACTIVE com.springsource.org.objectweb.asm_2.2.3
7 RESOLVED osgi_log_config_1.0.0
Master=36
8 ACTIVE org.springframework.bundle.osgi.core_1.0.1.v200803070100
9 ACTIVE com.springsource.slf4j.log4j_1.5.0
10 ACTIVE org.springframework.bundle.spring_2.5.2.v200803070100
11 ACTIVE org.springframework.bundle.spring.context_2.5.5
12 ACTIVE javax.servlet_2.4.0.v200706111738
13 ACTIVE org.springframework.osgi.servlet-api.osgi_2.5.0.SNAPSHOT
14 ACTIVE com.springsource.net.sf.cglib_2.1.3
15 ACTIVE org.springframework.bundle.spring.beans_2.5.5
16 ACTIVE javax.servlet.jsp_2.0.0.v200706191603
18 ACTIVE org.springframework.osgi.jetty.start.osgi_1.0.0
19 ACTIVE org.springframework.bundle.osgi.io_1.0.1.v200803070100
20 ACTIVE org.aopalliance_1.0.0
21 ACTIVE org.springframework.bundle.spring.context.support_2.5.5
23 ACTIVE com.springsource.org.aopalliance_1.0.0
24 ACTIVE org.springframework.bundle.spring.aop_2.5.5
25 ACTIVE com.springsource.slf4j.org.apache.commons.logging_1.5.0
30 ACTIVE org.objectweb.asm_2.2.3
33 ACTIVE org.mortbay.jetty.server_6.1.9
35 ACTIVE org.mortbay.jetty.util_6.1.9
36 ACTIVE org.springframework.osgi.log4j.osgi_1.2.15.SNAPSHOT
Fragments=7
37 ACTIVE org.mortbay.jetty_5.1.11.v200706111724
43 ACTIVE org.springframework.bundle.osgi.extender_1.1.2
44 ACTIVE org.springframework.bundle.osgi.io_1.1.2
45 ACTIVE org.springframework.bundle.osgi.web_1.1.2
46 ACTIVE org.springframework.bundle.osgi.web.extender_1.1.2
Fragments=2
47 ACTIVE org.springframework.bundle.osgi.core_1.1.2
以上這些Bundles可以在spring dm 1.1.2的發(fā)布包中找到,以上Bundles的start level設(shè)置為2。
2 加入Log4j日志配置Bundles
這個(gè)Bundles的目的在于提供log4j.properties,詳細(xì)做法可以參考本人的”spring osgi快速入門”
3 開發(fā)WEB應(yīng)用
WEB應(yīng)用的開發(fā)方式和普通的WEB基本上一樣,只是加入一些OSGi的配置。
大致結(jié)構(gòu)如下:
META-INF
MANIFEST.MF
WEB-INF
Classes
Lib
Web.xml
applicationContext.xml
1 MANIFEST.MF配置參考:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Osgi_web_app Plug-in
Bundle-SymbolicName: osgi_web_app
Bundle-Version: 1.0.0
Bundle-Vendor: ccsoft
Import-Package: javax.servlet,
javax.servlet.http,
javax.servlet.resources;version="2.4.0",
org.ccsoft.service,
org.springframework.osgi.web.context.support;version="1.1.2",
org.springframework.web.context,
org.springframework.web.context.support
Bundle-ClassPath: WEB-INF/classes/,
.
Require-Bundle: org.springframework.bundle.osgi.core,
org.springframework.bundle.osgi.io,
org.springframework.bundle.spring.beans,
org.springframework.bundle.spring.context,
org.springframework.bundle.spring.core
2 為了在web應(yīng)用中使用spring dm的IoC功能,web.xml中需要加入一些特定配置,類似于使用Spring時(shí)的配置,web.xml配置參考如下:
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<display-name>Simple Osgi WebApp Bundle</display-name>
<description>Simple OSGi War</description>
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>org.ccsoft.web.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/servlet</url-pattern>
</servlet-mapping>
</web-app>
至于applicationContext.xml則是標(biāo)準(zhǔn)的spring dm配置文件形式,只是沒有放在我們所熟悉的位置(META-INF/spring)
配置示例:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:osgi="http://www.springframework.org/schema/osgi"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd">
<osgi:reference id="HelloServiceOsgi" interface="org.ccsoft.service.SpeakService"/>
</beans>
在你的WEB應(yīng)用中可以使用如下代碼來訪問別的Bundle提供的服務(wù):
WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(req.getSession().getServletContext());
SpeakService ss=(SpeakService)ctx.getBean("HelloServiceOsgi");
與你使用Spring開發(fā)WEB應(yīng)用的寫法是完全一致的。
好了現(xiàn)在你可以利用spring dm開發(fā)你的web應(yīng)用了。更多相關(guān)問題還會(huì)在后續(xù)文章中逐一討論。
蔡超
軟件架構(gòu)師
Chao.cai@hp.com
Chaocai2001@yahoo.com.cn
致力于OSGi在中國的推廣
OSGi平臺(tái)為我們提供了強(qiáng)大的動(dòng)態(tài)特性,通過分析我們可以發(fā)現(xiàn)這些動(dòng)態(tài)特性的實(shí)現(xiàn)與很多常用的設(shè)計(jì)模式相關(guān),了解其中原理直接將這些模式用于我們的應(yīng)用開發(fā),也可以有效地實(shí)現(xiàn)動(dòng)態(tài)特性。
1 Broker模式:實(shí)現(xiàn)服務(wù)提供者與服務(wù)使用者的分離及解耦。Bundle通過所能提供的服務(wù)將自己注冊至Framework,調(diào)用者通過Framework查找所需的服務(wù)。Bundle的服務(wù)注冊是實(shí)現(xiàn)服務(wù)自動(dòng)發(fā)現(xiàn)的基礎(chǔ)。

2 監(jiān)聽者模式:這是實(shí)現(xiàn)動(dòng)態(tài)特性的關(guān)鍵,通過監(jiān)聽者模式服務(wù)的使用者(實(shí)現(xiàn)監(jiān)聽接口)可以獲得所依賴的服務(wù)提供者(Bundle)的狀態(tài)變化的通知,從而動(dòng)態(tài)處理與服務(wù)提供者間的關(guān)系以實(shí)現(xiàn)動(dòng)態(tài)特性,不僅如此OSGi Framework自身同樣有效的融合了這種通知機(jī)制,使得實(shí)現(xiàn)監(jiān)聽者接口的Bundle可以了解Framework的狀態(tài)變化。

在我們的應(yīng)用系統(tǒng)中借鑒OSGi的原理,同樣可以有效地實(shí)現(xiàn)動(dòng)態(tài)特性。
蔡超
軟件架構(gòu)師
軟件架構(gòu)顧問
SCEA,SCBCD
IBM Certified Solution Designer for OOA&D vUML2
Chaocai2001@yahoo.com.cn