利用Observer模式解决不同模块之间的交互

    技术2022-05-11  80

    利用Observer模式解决不同模块之间的交互

     

    最近在做一个基于NetBeans Platform技术的Java客户端应用。在该应用中,有一系列的窗口组件(TopComponent),每一个窗口组件位于一个NetBeans Module中。这些Module是相互独立的,不能互相引用。这些不同的Module里面的各种窗口组件之间可能有各种交互行为。

     

    以下面的业务场景来举例说明:

    在第一个ModuleWorkspaceModule中,有一个窗口组件WorkspaceTopComponent,双击打开其中的某一个Workspace(某种业务对象),就会引起另外两个Module中的若干窗口组件的变化:

    在第二个ModuleDocumentModule中,窗口组件DocumentTopComponent会显示打开的Workspace下的Document(某种业务对象)列表;

    在第三个ModuleJobModule中,窗口组件JobTopComponent会显示打开的Workspace下的Job(某种业务对象)列表。

    等等诸如此类的交互行为。

     

    一种做法是直接在WorkspaceModule中添加对DocumentModuleJobModule两个模块的引用,在WorkspaceTopComponent中直接操作DocumentTopComponentJobTopComponent,这种做法导致了这些模块之间的紧密耦合,失去了作为Module存在的意义。

     

    Eclipse RCP中有一个比较爽的扩展点机制(Extension Point),可以用来轻松解决这种问题,在NetBeans中尚不清楚有没有类似的机制,因此采用Observer模式来尝试解决这种问题。

     

    一、交互发起者和交互响应者接口

     

    我们将发起交互的组件视为被观察者(Subject),对交互进行响应的组件视为观察者(Observer)。在标准JDK中已经提供了Observable类和Observer接口来提供这种机制,但是Observable是一个类,限制了它的使用。在我们的应用中,我们用下面两个接口来表示:

     

    交互发起者接口ISponsor,它接受交互响应者的注册。

     

    public interface ISponsor {

       

        void register(IResponsor r);

       

        void unregister(IResponsor r);

       

        void unregisterAll();

    }

     

    交互响应者接口IResponsor,它要向感兴趣的交互发起者注册。

    public interface IResponsor {

           //这是一个标记接口,无方法

    }

     

    二、具体的交互发起者和具体的交互响应者接口

     

    上面的两个接口是业务无关的,只提供了注册的机制,不涉及到具体的交互通知和响应。具体到业务,需要由具体的交互发起者和具体的交互响应者来提供。

    在上面的业务场景中,WorkspaceTopComponent是一个具体的交互发起者,它会发起打开Workspace和关闭Workspace两种操作。为此提取出如下的接口:

    public interface IWorkspaceSponsor extends ISponsor{

          

        void notifyWorkspaceOpened(Workspace ws);

       

        void notifyWorkspaceClosed(Workspace ws);

    }

    DocumentTopComponentJobTopComponent是上述两种操作的具体的交互响应者。为此提取出如下的接口:

    public interface IWorkspaceResponsor extends IResponsor{

       

        void workspaceOpened(Workspace ws);

      

        void workspaceClosed(Workspace ws);

    }

     

    三、交互发起者注册表

     

    在我们的应用中,可能有众多的交互发起者和交互响应者,各种交互响应者如何找到它所感兴趣的交互发起者,这是一个问题。我们提供一个交互发起者注册表(SponsorRegistry)的单实例对象来提供这种功能:

    public class SponsorRegistry {

       

        private Map<String,ISponsor> smap=new HashMap<String,ISponsor>();

       

        private static SponsorRegistry registry=new SponsorRegistry();

       

        private SponsorRegistry() {

        }

       

        public static SponsorRegistry getRegistry(){

            return registry;

        }

       

        public void addSponsor(String sponsorName,ISponsor sponsor){

            smap.put(sponsorName,sponsor);

        }

       

        public void removeSponsor(String sponsorName){

            smap.remove(sponsorName);

        }

       

        public ISponsor getSponsor(String sponsorName){

            return smap.get(sponsorName);

        }

    }

     

    四、具体的交互发起者实现类和具体的交互响应者实现类

     

    在上面的业务场景中,WorkspaceTopComponent实现IWorkspaceSponsor接口,并在组件创建时将自身注册到SponsorRegistry中。

    //注:TopComponentNetBeans Platform中提供的一种窗口界面组件,它继承自JComponent,下面的代码中改为继承JComponent也应该没问题。

    public class WorkspaceTopComponent extends TopComponent implements IWorkspaceSponsor{

        private List<IWorkspaceResponsor> responsors=new ArrayList<IWorkspaceResponsor>();

     

        //下面是实现 ISponsor接口和IWorkspaceSponsor接口的相关方法

        public synchronized void register(IResponsor r){

            if(r instanceof IWorkspaceResponsor){

                responsors.add((IWorkspaceResponsor)r);

            }

            else{

                //...

            }

        }

       

        public synchronized void unregister(IResponsor r){

            if(r instanceof IWorkspaceResponsor){

                responsors.remove((IWorkspaceResponsor)r);

            }

            else{

                //...

            }

        }

       

        public synchronized void unregisterAll(){

            responsors.clear();

        }

       

        public void notifyWorkspaceOpened(Workspace ws){

            synchronized(this){

                Iterator<IWorkspaceResponsor> it=responsors.iterator();

                while(it.hasNext())

                    it.next().workspaceOpened(ws);

            }

        }

       

        public void notifyWorkspaceClosed(Workspace ws){

            synchronized(this){

                Iterator<IWorkspaceResponsor> it=responsors.iterator();

                while(it.hasNext())

                    it.next().workspaceClosed(ws);

            }

        }

     

        //当窗口创建时,将自身注册到SponsorRegistry

        public void componentOpened() {

            SponsorRegistry.getRegistry().addSponsor("WorkspaceSponsor",this);

        }

       

        //其他界面和逻辑的实现代码略

        //....

    }

     

    DocumentTopComponentJobTopComponent均实现IWorkspaceResponsor接口,并在组件创建时从SponsorRegistry中找到名为WorkspaceSponsor的交互发起者,注册上去。

    public class DocumentTopComponent extends TopComponent implements IWorkspaceSponsor{

     

        public void workspaceOpened(Workspace ws){

            //todo...

        }

       

        public void workspaceClosed(Workspace ws){

            //todo...

        }

     

        //当窗口创建时,找到Sponsor并注册上去

        public void componentOpened() {

            ISponsor s=SponsorRegistry.getRegistry().getSponsor("WorkspaceSponsor");

            s.register(this);

        }

       

        //当窗口关闭时,找到Sponsor并进行注销

        public void componentClosed() {

            ISponsor s=SponsorRegistry.getRegistry().getSponsor("WorkspaceSponsor");

            s.unregister(this);

        }

       

        //其他界面和逻辑的实现代码略

        //....

    }

     

    public class JobTopComponent extends TopComponent implements IWorkspaceSponsor{

     

        public void workspaceOpened(Workspace ws){

            //todo...

        }

       

        public void workspaceClosed(Workspace ws){

            //todo...

        }

     

        //当窗口创建时,找到Sponsor并注册上去

        public void componentOpened() {

            ISponsor s=SponsorRegistry.getRegistry().getSponsor("WorkspaceSponsor");

            s.register(this);

        }

       

        //当窗口关闭时,找到Sponsor并进行注销

        public void componentClosed() {

            ISponsor s=SponsorRegistry.getRegistry().getSponsor("WorkspaceSponsor");

            s.unregister(this);

        }

       

        //其他界面和逻辑的实现代码略

        //....

    }

     

    五、模块结构和依赖关系

     

    根据以上的描述,上述业务场景现在由如下几个Module组成:

    ObserverModule,提供交互发起者和交互响应者机制的的底层Module

    ISponsor接口、IResponsor接口位于此Module中,

    SponsorRegistry类也位于此Module中。

     

    BusinessModule,管理各种具体的交互发起者接口和交互响应者接口的Module

    IWorkspaceSponsor接口、IWorkspaceResponsor接口位于此Module中,

    Workspace类等公共的业务对象也位于此Module中。

     

    WorkspaceModuleWorkspace业务功能所在模块。

    WorkspaceTopComponent位于此Module中。

     

    DocumentModuleDocument业务功能所在模块。

    DocumentTopComponent位于此Module中。

     

    JobModuleJob业务功能所在模块。

    JobTopComponent位于此Module中。

     

    下图是以上模块的依赖关系示意图:

     

     

     

     

    最新回复(0)