模塊的封裝性分析-讀書筆記

引子

最近看《Java Application Architecture-Modularity Patterns with Examples Using OSGi-中文譯名Java應(yīng)用架構(gòu)設(shè)計》時,在物理模塊的封裝設(shè)計方面深受啟發(fā)。市面上很多書都是介紹類的封裝、訪問性控制、邏輯設(shè)計,但是關(guān)于物理模塊的設(shè)計的書并不多見,這本書是這方面的好書,有Bob大叔推薦作序。同時此書推薦的一本老書《Large-Scale C++ Software Design》也是這方面的經(jīng)典好書。就像這本書所說的缺乏物理設(shè)計的邏輯設(shè)計并不會帶來預(yù)期的影響,深有同感。

問題描述

討論一個和參考資料[1]中3.4節(jié)、[2]、[3]中描述的問題相同的簡化問題。根據(jù)面向接口編程的理念,提供服務(wù)的模塊只暴露服務(wù)接口,隱藏實現(xiàn)??蛻舳四K以接口訪問服務(wù)端模塊的服務(wù)??蛻舳四K中不能出現(xiàn)任何具體實現(xiàn)類的引用耦合。這樣便于以后改變服務(wù)端模塊實現(xiàn)的同時不影響客戶端模塊。我們希望具體實現(xiàn)類對客戶端模塊不可見。這樣在提供服務(wù)端模塊時強制以接口公開服務(wù)。

解決方法

[1]中采用Spring框架注入實現(xiàn)類,[3]中描述了采用ServiceLoader和META-INF注入實現(xiàn)類。以下以[2]中類似的代碼為例介紹Java的方法,此處沒有采用框架,僅僅是用了一個簡單的工廠控制實現(xiàn)類的注入。并和.NET的解決方法對比。這里采用參考資料[1]中的模塊定義,Java定義jar文件為物理模塊單元,.NET定義程序集dll文件為物理模塊單元,這也是我們平時常用引用第三方類庫的方法。

Java的解決方法

服務(wù)接口

package org.p2.helloworld;

public interface HelloService {  
void sayHello(String name);  
}

實現(xiàn)類

package org.p2.helloworld.impl;

import org.p2.helloworld.HelloService;

public class HelloServiceImpl implements HelloService {
public void sayHello(String name){
    System.out.println("Hello," + name);
}
}

簡單的工廠控制實現(xiàn)

package org.p2.helloworld;

import org.p2.helloworld.impl.HelloServiceImpl;

public class HelloFactory {
    public static HelloService getHelloService() {
        return new HelloServiceImpl();
    }
}

服務(wù)模塊單元provider.jar包含以上幾個包,客戶端client.jar內(nèi)容如下。

package org.p1.helloworld.main

import org.p2.helloworld.*;

public class Main {
public static void main(String[] args) {
    HelloService helloService = HelloFactory.getHelloService();
    helloService.sayHello("World"); 
}
}

以上實現(xiàn)由于將接口和實現(xiàn)類放在了不同的包中,所以實現(xiàn)類可見性必須為public。如果將實現(xiàn)類和接口放在同一個包中,則實現(xiàn)類可見性可設(shè)置為僅包可見。實現(xiàn)類代碼如下,其他類同上,可實現(xiàn)provider.jar僅向外暴露接口。

class HelloServiceImpl implements HelloService {
public void sayHello(String name){
    System.out.println("Hello," + name);
}
}

.NET解決方案討論

服務(wù)接口

namespace Provider
{
    public interface HelloService
    {
        void SayHello(string name);
    }
}

實現(xiàn)

namespace Provider.Impl
{
    internal class HelloServiceImpl : HelloService
    {
        public void SayHello(string name)
        {
            Console.WriteLine("Hello," + name);
        }
    }
}

工廠類

using Provider.Impl;
namespace Provider
{
    public class HelloServiceFactory
    {
        public static HelloService GetHelloService()
        {
            return new HelloServiceImpl();
        }
    }
}

以上為服務(wù)模塊單元provider.dll包含內(nèi)容,.NET下可實現(xiàn)將實現(xiàn)類和接口放在不同命名空間下,同時向外僅暴露接口。

分析討論

以上將HelloService接口和實現(xiàn)類HelloServiceImpl放在了同一個物理provider.jar下的兩個包下,以provider.jar類庫形式提供給客戶端。無論是采用[1]中的Spring框架注入實現(xiàn)類,還是[3]中的方法注入實現(xiàn)類,如果將實現(xiàn)類和接口放在不同包中,都必須將HelloServiceImpl的包可見性設(shè)置為public。這樣導致了一旦客戶端模塊引用了服務(wù)端模塊并導入包,則可直接實例化實現(xiàn)類。這就是參考資料里所說的Java沒有提供將包或類定義為模塊作用域的方法,導致一個模塊中的類總是能夠訪問另一個模塊的實現(xiàn)細節(jié),這也OSGi這樣的框架致力解決的問題。如果將實現(xiàn)類和接口放在一個包中則可以向外僅暴露接口,但同一物理模塊jar下的其他包無法使用實現(xiàn)類。
對比.NET下的解決方法,.NET下可以將實現(xiàn)類設(shè)置為internal,物理模塊內(nèi)可見,對引用此模塊的客戶端程序不可見。而Java的包可見性有邏輯方面和物理設(shè)計方面的限制,并不是很純粹。因此java的包和.NET的命名空間有很大不同。個人猜測這可能和跨平臺有關(guān),畢竟物理模塊和具體平臺有關(guān)。結(jié)合實際的應(yīng)用情況,確實需要物理方面的可見性控制,這樣才能提供更好的封裝性。物理模塊的組織設(shè)計及良好的封裝性確實是這本書給我最大的啟示。

參考資料

  1. Java Application Architecture-Modularity Patterns with Examples Using OSGi, Kirk Knoernschild, 中文譯名Java應(yīng)用架構(gòu)設(shè)計-模塊化與OSGi
  2. OSGi中文社區(qū),OSGi入門篇:模塊層
  3. OSGi:簡介,優(yōu)酷jevon_fu
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,695評論 19 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,286評論 6 342
  • 果寶辛麗閱讀 316評論 0 0
  • 也許,在你的記憶中,意大利在二戰(zhàn)中顏面盡失,成為歷史的笑料,然而也許你并不曾知道,在晚清,中國國勢最衰弱的時候,意...
    冷墨瀟染閱讀 4,547評論 14 17

友情鏈接更多精彩內(nèi)容