繼承和面向接口(iOS架構思想篇)

2021-02-20 Cocoa開發者社區

前言

在開篇之前思考幾個問題?

1、繼承最大的缺點是什麼?

2、為什麼說耦合也可能是一種需求?

3、有哪些場景不適合使用繼承?

4、繼承本身就具有高耦合性,但卻可以實現代碼復用,有哪些替代方案可以去除高耦合性並實現代碼的復用?

5、iOS 開發中有否有必要同一派生 ViewController?

6、什麼是面向切面編程思想?

7、為什麼Swift著力宣傳面向協議的思想,而OC 中面向協議的思想為什麼不能像Swift那樣得以普及?

8、函數式鏈式編程中如何對外控制函數調用的先後順序?如:Masonry (面向接口解決問題)
在接下來的分析中,這些問題都會一一得到解答,保證乾貨滿滿。筆者原本想著圍繞繼承和面向接口各寫一片文章,但實際繼承和面向接口在某些方面還有很多的關聯性,因此這裡索性合二為一。

一、繼承 (優缺點、使用原則、替代方案)
二、ViewController是否應統一繼承
三、面向接口思想
四、多態和面向接口的選擇
五、面向接口實現順序控制


一、繼承1.1 繼承的優缺點


繼承、封裝、多態是面向對象的三大支柱。關於繼承毫無疑問最大的優點是代碼復用。但是很多時候繼承也可能會被無止境的濫用,造成代碼結構散亂,後期維護困難等,其中有可能帶來最大的問題是高耦合。

1.2 繼承的使用的原則

假設你的代碼是針對多平臺多版本的,並且你需要針對每個平臺每個版本寫一些代碼。這時候更合理的做法可能是創建一個 OBJDevice 類,讓一些子類如 OBJIPhoneDevice 和 OBJIPadDevice ,甚至更深層的子類如 OBJIPhone5Device 來繼承,並讓這些子類重寫特定的方法。關於這個場景就非常適合使用繼承,因為總的來說它滿足如下條件:

父類OBJDevice只是給其他派生的子類提供服務,OBJDevice只做自己分內的事情,並不涉及子類的業務邏輯。不同的業務邏輯由不同的子類自己去完成。子類和父類各做自身的事情,互不影響和幹擾。

父類OBJDevice 的變化要在所有子類中得以體現。也就是說父類牽一動發全部子類,可以理解為此時的高耦合是一種需求,而不是一種缺點。
如果滿足上述兩種條件,可以考慮使用繼承。另外,實際開發中如果繼承超過2層的時候,就要慎重這個繼承的方案了,因為這可能是濫用繼承的開始。


1.3 替代繼承的方式

針對不適合用繼承來做的事,或不想用繼承來做的,還有如下幾種備選方案可以適合不同的場景,有利於打開你的思路。

1.3.1 協議

假設原本已經開發了一個繼承NSObject的音頻播放器VoicePlayer,但此時想支持OGG格式的音頻。而實際上之前的VoicePlayer和現在想要開發的音頻播放器類,只是對外提供的API類似,內部實現代碼卻差別很大。這裡簡單說明一下OGG格式音頻在遊戲開發中用的比較普遍,筆者之用原生開發一款遊戲應用時,就曾使用過OGG格式音頻,相比於其他音頻而言,OGG最大的特點是體積更小。一段音頻中,沒有聲音的那一部分將不暫用任何體積,而類似MP3格式則不同,即使是沒聲音,依然會存在體積佔用。參照上面關於繼承的使用原則可知,此時繼承並不適合這種場景。筆者給出的答案是通過協議提供相同的接口,代碼結構如下:

@protocol VoicePlayerProtocol <NSObject>
- (void)play;
- (void)pause;
@end

@class NormalVoicePlayer : NSObject <VideoPlayerProtocol>
@end

@class OGGVoicePlayer : NSObject <VideoPlayerProtocol>
@end

1.3.2 用組合替代繼承

如果想重用已有的代碼而不想共享同樣的接口,組合便是首選。

假如:A界面有個輸入框,會根據伺服器上用戶的輸入歷史來自動補全,叫AutoCompleteTextField。後來某天來了個需求,在另外一個界面中,也用到這個輸入框,除了根據輸入歷史補全,增加一個自動補全郵箱的功能,就是用戶輸入@後,我們自動補全一些域名。這個功能很簡單,結構如下:

@interface AutoCompleteTextField : UITextField
- (void)autoCompleteWithUserInfo;
@end

@interface AutoCompleteMailTextField : AutoCompleteTextField
- (void)autoCompleteWithMail;
@end

過兩天,產品經理希望有個本地輸入框能夠根據本地用戶信息來補全,而不是根據伺服器的信息來自動補全,我們可以輕鬆通過覆蓋來實現:

@interface AutoCompleteLocalTextField : AutoCompleteTextField- (void) autoCompleteWithUserInfo;@end

app上線一段時間之後,UED不知哪根筋搭錯了,決定要修改搜索框的UI,於是添加個初始化函數initWithStyle得以解決。


重點來了,但是有一天,隔壁項目組的哥們想把我們的本地補全輸入框AutoCompleteLocalTextField移植到他們的項目中。這個可就麻煩了,因為使用AutoCompleteLocalTextField要引入AutoCompleteTextField,而AutoCompleteTextField本身也帶著API相關的對象,同時還有數據解析的對象。 也就是說,要想給另外一個TEAM,差不多整個網絡層框架都要移植過去。

上面這個問題總結來說是兩種類型問題:第一種類型問題是改了一處,其他都要改,但是勉強還能接受;第二種類型就是代碼服用的時候,要把所有相關依賴都拷貝過去才能解決問題;兩種類型的問題都說明了繼承的高耦合性,牽一而動全身的特性。


關於上述問題最佳的解決方案,筆者認為是通過組合的形式,區分不同的模塊來處理,輸入框本身的UI可以作為一個模塊,本地搜索提示和伺服器搜索提示可以作為不同的模塊分別處理。實際使用中可以通過不同的模塊組合,實現不同的功能。

1.3.3 類別

有時可能會想在一個對象的基礎上增加額外的功能形成另外一個對象,繼承是一種很容易想到的方法。還有另外一種比較好的方案是通過類別。為該對象擴展方法,按需調用,比如為NSArray增加一個移除第一個元素的方法:

@interface NSArray (OBJExtras)- (void)removingFirstObject;@end

1.3.4 配置對象

假設某個app中有主題切換,其中每種主題都對應backgroundColor 和 font 兩個屬性。按照繼承的思路我們很有可能會先寫一個父類,為這個父類實現一個空的setupStyle方法,然後各種不同風格的主題分別是一個子類,重寫父類的setupStyle方法。

其實大可不必這樣做,完全可以創建一個ThemeConfiguration的類,該類中具有 backgroundColor和 fontSize 屬性。可以事先創建幾種主題, Theme 在其初始化函數中獲取一個配置類 ThemeConfiguration 的值即可。相比繼承而言,就不用創建那麼多文件,以及父類中還要寫一個 setupStyle空方法。

二、ViewController是否應統一繼承2.1 不統一繼承的理由

如果ViewController統一繼承了父類控制器,首先可能會涉及到上面說到的高耦合的一個項目,缺點;除此之外,還會涉及上手接受成本問題,新手接受需要對父類控制器的使用有一定的了解;另外,如果涉及項目遷移問題,在遷移子類控制器的同時還要將父類控制器也遷移出去。最後一個理由是,即使不通過繼承,同樣能達到對項目控制器進行統一配置。

2.2 面向切面(AOP)思想簡介

上面也說了幾種替代繼承的方法,如果ViewController不通過繼承的方式實現,那麼首選的替代方式是什麼?這裡我們可以採用面向切面的編程思想和分類結合的方式替代控制器的繼承。


首先簡單說下面向切面的編程思想(AOP),聽起來很高大上,實際上很多iOS開發者應該都用過,在iOS中最直接的體現就是藉助 Method Swizzling 實現方法替換。一般,主要的功能是日誌記錄,性能統計,安全控制,事務處理,異常處理等等。主要的意圖是:將日誌記錄,性能統計,安全控制,事務處理,異常處理等代碼從業務邏輯代碼中劃分出來,通過對這些行為的分離,我們希望可以將它們獨立到非指導業務邏輯的方法中,進而改 變這些行為的時候不影響業務邏輯的代碼。可以通過預編譯方式和運行期動態代理實現在不修改原始碼的情況下給程序動態統一添加功能的一種技術。假設把應用程式想成一個立體結構的話,OOP的利刃是縱向切入系統,把系統劃分為很多個模塊(如:用戶模塊,文章模塊等等),而AOP的利刃是橫向切入系統,提取各個模塊可能都要重複操作的部分(如:權限檢查,日誌記錄等等)。


2.3 方案實現

面向切面的思想可以實現系統資源的統一配置,iOS 中的Method Swizzling替換系統方法可達到同樣的效果。這裡筆者更為推薦使用第三方開源庫Aspects去攔截系統方法。

我們可以創建一個叫做ViewControllerConfigure的類,實現如下代碼。


{
    [super load];
    [ViewControllerConfigure sharedInstance];
}

+ (instancetype)sharedInstance
{    static dispatch_once_t onceToken;    static ViewControllerConfigure *sharedInstance;    dispatch_once(&onceToken, ^{
        sharedInstance = [[ViewControllerConfigure alloc] init];
    });    return sharedInstance;
}

- (instancetype)init
{    self = [super init];    if (self) {        
        [UIViewController aspect_hookSelector:@selector(loadView) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo>aspectInfo){
            [self loadView:[aspectInfo instance]];
        } error:NULL];

        [UIViewController aspect_hookSelector:@selector(viewWillAppear:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo, BOOL animated){
            [self viewWillAppear:animated viewController:[aspectInfo instance]];
        } error:NULL];
    }    return self;
}#pragma mark - fake methods- (void)loadView:(UIViewController *)viewController
{    NSLog(@" loadView");
}

- (void)viewWillAppear:(BOOL)animated viewController:(UIViewController *)viewController
{    
    NSLog(@"viewWillAppear");
}@end

關於上面的代碼主要說三點:

1、藉助 load 方法,實現代碼無任何入性型。
當類被引用進項目的時候就會執行load函數(在main函數開始執行之前),與這個類是否被用到無關,每個類的load函數只會自動調用一次。除了這個案列,在實際開發中筆者曾這麼用過load方法,將app啟動後的廣告邏輯相關代碼全部放到一個類中的load方法,實現廣告模塊對項目的無入侵性。initialize在類或者其子類的第一個方法被調用前調用。即使類文件被引用進項目,但是沒有使用,initialize不會被調用。由於是系統自動調用,也不需要再調用 [super initialize] ,否則父類的initialize會被多次執行。
2、不單單可以替換loadView和viewWillAppear方法,還可以替換控制器其他生命周期相關方法,在這些方法中實現對控制器的統一配置。如view背景顏色、統計事件等。
3、控制器中避免不了還會拓展一些方法,如無網絡數據提示圖相關方法,此時可以藉助Category實現,在無法避免使用屬性的情況下,可以藉助運行時添加屬性。

關於控制器的集成問題就先說到這,接下來看看面向接口的思想。

三、面向接口思想

對於接口這一概念的支持,不同語言的實現形式不同。Java中,由於不支持多重繼承,因此提供了一個Interface關鍵詞。而在C++中,通常是通過定義抽象基類的方式來實現接口定義的。Objective-C既不支持多重繼承,也沒有使用Interface關鍵詞作為接口的實現(Interface作為類的聲明來使用),而是通過抽象基類和協議(protocol)來共同實現接口的。OC中接口可以理解為Protocol,面向接口編程可以理解為面向協議編程。先看如下兩端代碼:

ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDidFinishSelector:@selector(requestDone:)];
[request setDidFailSelector:@selector(requestWrong:)];
[request startAsynchronous];

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:@"www.olinone.com" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {

}];

觀察上述兩段代碼,是否發現第二段網絡請求代碼相比第一段更容易使用。因為第二段代碼只需初始化對象,然後調用方法傳參即可,而第一段代碼要先初始化,然後設置一堆屬性,最終才能發起網絡請求。如果讓一個新手上手,毫無疑問更喜歡採用第二種方式調用方法,因為無需對AFN掌握太多,僅記住這一個方法便可發起網絡請求,而反觀 ASI 要先了解並設置各種屬性參數,最終才能發起網絡請求。上面的兩端代碼並不是為了說明ASI和AFN熟好熟劣,只是想藉此引出面向接口的思想。

所以,通過接口的定義,調用者可以忽略對象的屬性,聚焦於其提供的接口和功能上。程序猿在首次接觸陌生的某個對象時,接口往往比屬性更加直觀明了,抽象接口往往比定義屬性更能描述想做的事情。

相比於OC,Swift 可以做到協議方法的具體實現,而 OC 則不行。面向對象編程和面向協議編程最明顯的區別在於程序設計過程中對數據類型的抽取(抽象)上,面向對象編程使用類和繼承的手段,數據類型是引用類型;而面向協議編程使用的是遵守協議的手段,數據類型是值類型(Swift中的結構體或枚舉)。看一個簡單的swift版面向協議範例,加入想為若干個繼承自UIView的控制項擴展一個抖動動畫方法,可以按照如下代碼實現:


import UIKit
protocol Shakeable { }
extension Shakeable where Self: UIView {
    func shake() {
        
    }
}

如果想實現這個shake動畫,相關控制項只要遵守這個協議就可以了。

class CustomImageView: UIImageView, Shakeable {

}class CustomButton: UIButton, Shakeable {

}

可能有的人就會問了,直接通過 extension實現不就可以了,這種方案是可以的。但是,如果使用extension方式對於 CustomImageView 和 CustomButton,根本看不出來任何抖動的意圖,整個類裡面沒有任何東西能告訴你它需要抖動。相反,通過協議可以很直白的看出抖動的意圖。這僅僅是面向協議的一個小小好處,除此之外在Swift中還有很多巧妙的用法。

import UIKit
extension UIView {
    func shake() {
    }
}

四、多態和面向接口的選擇4.1 多態

不同對象以自己的方式響應相同的消息的能力叫做多態。OC中最直接的體現就是父類指針指向子類對象,如:Animal *a = [Dog new];Dog *d = (Dog *)a; [d eat];。


4.2 多態和面向接口的對比

前段時間看了Casa大神的跳出面向對象思想受益不少。所以想把自己所理解的用文字的形式記錄下來。以一個文件解析類為例,文件解析的過程中主要有兩個步驟:讀取文件和解析文件。假如實際中可能會有一些格式十分特殊的文件,所用到的文件讀取方式和解析方式不同於常規方式。通常按照繼承的寫法可能會是下面這樣。


@interface FileParseTool : NSObject
- (void)parse;
- (void)analyze;
@end


@implementation FileParseTool
- (void)parse {
    [self readFile];
    [self analyze];
}
- (void)readFile {
    
    ....
}
- (void)analyze {
    
}
@end

如果想實現對特殊格式文件的解析,此時可能會重寫父類的analyze方法。

@interface SpecialFileParseTool: FileParseTool

@end

@implementation SpecialFileParseToll
- (void)analyze {
    NSLog(@"%@:%s", NSStringFromClass([self class]), __FUNCTION__);
}
@end

按照繼承的寫法,會存在以下問題:

使用面向接口的方式實現代碼如下:


@protocol FileParseProtocol <NSObject>
- (void)readFile;
- (void)analyze;
@end

@interface FileParseTool : NSObject
@property (nonatomic, weak) id<FileParseProtocol> assistant;
- (void)parse;
@end


@implementation FileParseTool
- (void)parse {
    [self.assistant readFile];
    [self.assistant analyze];
}
@end


@interface SpecialFileParseTool: FileParseTool <FileParseProtocol>
@end


@implementation SpecialFileParseTool
- (instancetype)init {
    self = [super init];
    if (self) {
        self.assistant = self;
    }
    return self;
}
- (void)analyze {
    NSLog(@"analyze special  file");
}
- (void)readFile {
    NSLog(@"read special file");
}
@end

相比較於繼承的寫法,面向接口的寫法恰好能彌補上述三個缺陷:

父類中將不會再用analyze空方法掛在那裡。

原本需要覆蓋重載的方法,不放在父類的聲明中,而是放在接口中去實現。基於此,公司內部可以規定:不允許覆蓋重載父類中的方法、子類需要實現接口協議中的方法,可以避免繼承上帶來的困惑。子類中如果引入了父類的外部邏輯,此時通過協議的控制,原本引入了不相關的邏輯也很容易被剝離。


4.3 面向接口如何解決case大神的四個問題

casa提出使用多態面臨的四個問題:

父類有部分public的方法是不需要,也不允許子類覆重。

父類有一些特別的方法是必須要子類去覆重的,在父類的方法其實是個空方法。

父類有一些方法即便被覆重,父類原方法還是要執行的。

父類有一些方法是可選覆重的,一旦覆重,則以子類為準。
接著結合上述第二種方式,說說是如何解決這四個問題的。

關於第一個問題,在利用面向接口的方案中,公司內部可以規定:不允許覆蓋重載父類中的方法、子類需要實現接口協議中的方法。

關於第二個問題,第二個方案中父類FileParseTool的.m文件中不再存在空的analyze方法。

關於第三個問題,顯然能在解答第一個問題中找到答案。

關於第四個問題,可能需要再補充一些代碼來解決這個問題。主要思路是:通過在接口中設置哪些方法是必須要實現,哪些方法是可選實現的來處理對應的問題,由子類根據具體情況進行覆重。代碼如下:



@protocol FileParseProtocol <NSObject>
- (void)readFile;
- (void)analyze;
@end


@protocol InterceptorProtocol <NSObject>
- (void)willBeginAnalyze;
- (void)didFinishAnalyze;
@end

@interface FileParseTool : NSObject
@property (nonatomic, weak) id<FileParseProtocol> assistant;
@property (nonatomic, weak) id<InterceptorProtocol> interceptor;
- (void)parse;
@end


@implementation FileParseTool
- (void)parse {
    [self.assistant readFile];
    if ([self.interceptor respondsToSelector:@selector(willBeginAnalyze)]) {
        [self.interceptor willBeginAnalyze];
    }
    [self.assistant analyze];
    if ([self.interceptor respondsToSelector:@selector(didFinishAnalyze)]) {
        [self.interceptor didFinishAnalyze];
    }
}
@end


@interface SpecialFileParseTool: FileParseTool<FileParseProtocol,InterceptorProtocol>
@end


@implementation SpecialFileParseTool
- (instancetype)init {
    self = [super init];
    if (self) {
        self.assistant = self;
        self.interceptor = self;
    }
    return self;
}
- (void)analyze {
    NSLog(@"analyze special  file");
}
- (void)readFile {
    NSLog(@"read special file");
}
@end

4.4 何時使用多態
五、面向接口實現順序控制5.1 函數式和鏈式編程思想

在次之前先簡單說下類似Masonry框架的函數式和鏈式編程的實現思路。


5.2 函數式和鏈式實現

假如封裝一個資料庫管理工具類,藉助函數式和鏈式編程思想,外部的調用形式可以是這樣:

NSString *sql = [SQLTool makeSQL:^(SQLTool *tool) {
        tool.select(nil).from(@"").where(@"");
 }];

代碼的實現可以是這樣:


#import <Foundation/Foundation.h>
@class SQLTool;

typedef SQLTool *(^Select)(NSArray<NSString *> *columns);
typedef SQLTool *(^From) (NSString *tableName);
typedef SQLTool *(^Where)(NSString *conditionStr);

@interface SQLTool : NSObject

@property (nonatomic, strong, readonly) Select select;
@property (nonatomic, strong, readonly) From from;
@property (nonatomic, strong, readonly) Where where;

+ (NSString *)makeSQL:(void(^)(SQLTool *tool))block;
@end


#import "SQLTool.h"
@interface SQLTool()
@property (nonatomic, strong) NSString *sql;
@end
@implementation SQLTool
+ (NSString *)makeSQL:(void(^)(SQLTool *tool))block {
    if (block) {
        SQLTool *tool = [[SQLTool alloc] init];
        block(tool);
        return tool.sql;
    }
    return nil;
}
- (Select)select {
    return ^(NSArray<NSString *> *columns) {
        self.sql = @"select 篩選的結果";
        
        return self;
    };
}
- (From)from{
    return ^(NSString *tableName) {
        self.sql = @"from 篩選的結果";
        return self;
    };
}
- (Where)where{
    return ^(NSString *conditionStr){
       self.sql = @"where 篩選的結果";
        return self;
    };
}
@end

雖然實現了函數式和鏈式編程思想,但是如果想讓外界調用者嚴格按照select、from、where的順序去掉用,而不是毫無順序的胡亂調用,請問這種情況該如何處理?下面會藉助面向協議編程思想給出答案。


5.3 實現順序控制

關於上面的順序調用的問題,我們可以這樣想:某個類遵從了某個協議,從一定程度上講就等同於這個類就有了協議中聲明的方法可供外界調用。如果反過來,如果沒有遵從協議就無法調用了。ps:此處所說的調用,只是從編譯的角度出發。具體實現請看下面代碼,總的來說沒有太高深的語法相關問題。


#import <Foundation/Foundation.h>
@class SQLToolTwo;
@protocol ISelectable;//1、
@protocol IFromable;//2、
@protocol IWhereable;//3、

typedef SQLToolTwo<IFromable>*(^SelectTwo)(NSArray<NSString *> *columns);
typedef SQLToolTwo <IWhereable>*(^FromTwo)(NSString *tableName);
typedef SQLToolTwo *(^WhereTwo) (NSString *conditionStr);

@protocol ISelectable <NSObject>
@property (nonatomic, copy, readonly) SelectTwo selectTwo;
@end

@protocol IFromable <NSObject>
@property (nonatomic, copy, readonly) FromTwo fromTwo;
@end

@protocol IWhereable <NSObject>
@property (nonatomic, copy, readonly) WhereTwo whereTwo;
@end

@interface SQLToolTwo : NSObject
+ (NSString *)makeSQL:(void(^)(SQLToolTwo<ISelectable> *tool))block;
@end


#import "SQLToolTwo.h"
@interface SQLToolTwo()<ISelectable, IFromable, IWhereable>
@property (nonatomic, strong) NSString *sql;
@end

@implementation SQLToolTwo
+ (NSString *)makeSQL:(void(^)(SQLToolTwo<ISelectable> *tool))block {
    if (block) {
        SQLToolTwo*tool = [[SQLToolTwo alloc] init];
        block(tool);
        return tool.sql;
    }
    return nil;
}
- (SelectTwo)selectTwo {
    return ^(NSArray<NSString *> *columns) {
        self.sql = @"select 篩選的結果";
        return self;
    };
}
- (FromTwo)fromTwo{
    return ^(NSString *tableName) {
        self.sql = @"from 篩選的結果";
        return self;
    };
}
- (WhereTwo)whereTwo{
    return ^(NSString *conditionStr){
        self.sql = @"where 篩選的結果";
        return self;
    };
}
@end

按照上述實現代碼,你將只能嚴格按照selectTwo、fromTwo、whereTwo的順序執行代碼。這是因為美調用一次相關的block,返回的SQLToolTwo實例對象遵守不同的協議。

NSString *sql2 = [SQLToolTwo makeSQL:^(SQLToolTwo<ISelectable> *tool) {
        tool.selectTwo(nil).fromTwo(@"").whereTwo(@"");
   }];

六、總結


文章的第一部分首先說了繼承的代碼復用性和高耦合性,然後總結了繼承應當在何時使用,最後有說了四種替代繼承的方案(協議組合類別配置對象);第二部分利用面向切面的思想,解決了iOS開發中關於ViewController繼承的問題;第三部分簡單介紹了面向接口的思想,以及和面向對象思想的比較;第四部分涉及多態和面向接口的抉擇問題;第五部分的實現代碼中包含函數式、鏈式以及面向接口的思想,其中重點說明了如何利用面向接口的思想控制函數的執行流程順序問題。

作者:ZhengYaWei
連結:https://www.jianshu.com/p/39e6a8409476

相關推薦:

相關焦點

  • 軟體項目實訓及課程設計指導——如何實現面向對象的系統架構設計
    2、在軟體應用系統的架構設計中要綜合應用OOP、AOP和SOA相關的設計思想(1)面向對象(OOP)和面向方面(AOP)的設計思想由於與面向對象(OOP)和面向方面(AOP)開發有關的各種設計思想和具體的實現技術都是解決軟體應用系統的內部結構設計方面的問題
  • 從分而治之的思想到架構的設計
    若不調整架構並要兼容之前架構未作適配的業務的話,那只能大幅增加模塊間的耦合(接口)因此個人覺得大規模的拆分,最理想的情況下,應該是在同一個jar的粒度下,先做好基於module/pakcage的拆分及重構,然後穩定運行一定程度後,如果仍然覺得有拆分的必要的話,再將其進行jar、進程級別的隔離。
  • 繼承、接口與多態的相關問題
    繼承而得到的類稱為子類,被繼承的類稱為父類。子類不能繼承父類中訪問權限為private的成員變量和方法。子類可以重寫父類的方法,及命名與父類同名的成員變量。但Java不支持多重繼承,即一個類從多個超類派生的能力。
  • 淺析java繼承與多態,抽象類與接口的區別
    Java 是個純粹的面向對象語言,沒有過程式的寫法,所有操作都是基於對象。面向對象的設計可以更好的解決系統維護、擴展、和代碼重用的需求,代碼過多後過程式的邏輯組織會變得異常複雜,所以就發展出了面向對象式編程。而面向對象的基本特性就是封裝、繼承、與多態。
  • 軟體項目實訓及課程設計指導——如何實現面向服務的系統架構設計
    )其實就是一種新型的軟體應用系統的系統體系架構設計形式,在基於SOA架構設計思想所開發實現的企業級軟體應用系統中,軟體應用系統中的各個業務功能的程序模塊是由一些鬆散耦合併且具有統一接口定義的服務組件相互組合而構建的。
  • Java程式設計師如何理解繼承和接口這兩個概念
    比如在Web開發、移動網際網路開發和大數據開發時代,Java語言都能夠得到大量的使用,這其中接口起到了非常關鍵的作用。Java中的接口和繼承並不是解決同樣的問題,實際上,Java語言當中的繼承在很多場景下並不建議使用,原因是繼承本身屬於「強耦合」的概念,強耦合本身有非常多的弊端,為程序的復用和維護也帶來了一定的困難。當然,繼承作為面向對象語言的三大特徵之一,如果合理地運用,也會帶來很多方便,也會更全面地描述各種模型。
  • iOS開發之淺談MVVM的架構設計與團隊協作
    demo:https://github.com/lizelu/MVVM今天寫這篇文章是想達到拋磚引玉的作用,想與大家交流一下思想,相互學習,博文中有不足之處還望大家批評指正。本篇文章的內容沿襲以往博客的風格,也是以乾貨為主,偶爾扯扯鹹蛋(哈哈~不好好工作又開始發表博客啦~)。
  • Java之接口之間的多繼承
    各位小夥伴們大家好,這次小編要介紹的是Java當中,接口的多繼承。具體如下:1.類與類之間是單繼承的,直接父類只有一個。2.類與接口之間是多實現的,一個類可以實現多個接口。3.接口與接口之間是多繼承的。注意事項:1.多個父接口之間的抽象方法可以重複。
  • 架構設計:企業總體架構要如何做?小白也能快速領悟的設計思想
    一個應用架構設計的形成不單單是技術上,是統籌性的輸出,一般分為:功能清單,用例及活動圖,領域圖,接口設計,分層設計,業務代碼,其他設計。在一個商務模型裡面,基本涉及了主營業務,商務模式,運營模式,主營產品,競品分析和業務流程等。很多公司在開始做應用軟體之前,整體的商務模式基本上已經是清楚的,此時的設計階段就趨向於需求調研階段。
  • 再談面向對象的設計原則
    談到面向對象,們毫不猶豫的說出面向對象的三大特徵:封裝、繼承、多態,面向對象這在軟體的構架設計中是非常重要的,最能直接體現出來的優點就是軟體的擴展性和重用性。要理解面向對象編程,只停留在開發層面上,是無法深入理解的。只有在設計層面上展開才能逐步的領會面向對象。但在實際的開發中,並不是每一個人都能夠接觸到核心的設計工作。
  • SOA(面向服務的體系架構)基本概念
    1.1 SOA的基本概念SOA是英文詞語"Service Oriented Architecture"的縮寫,中文有多種翻譯,如"面向服務的體系結構"、"以服務為中心的體系結構"和"面向服務的架構",其中"面向服務的架構"比較常見。
  • 手把手Java入門:繼承(面向對象篇)
    本篇轉自CSDN博客博主的JAVA入門基礎,讀者可以點擊原文連結進入博客,閱讀作者關於JAVA基礎的其他文章,希望對您有幫助。繼承的概念繼承是java面向對象編程中的基石,它允許創建分層次的類。繼承是子類繼承父類的特徵和行為,使得子類對象具有父類的實例域和方法,或者子類直接繼承父類的方法,使得子類具有父類相同的行為。生活中的繼承老鼠和貓都是動物類,老鼠和貓就是動物類的子類,而動物類就是父類,繼承的符合關係是:is-a.在java中實現繼承的關鍵字是extends,它可以聲明一個類是從另外一個類繼承而來的。
  • Go 語言中無心插柳柳成蔭的接口和無為而治的空接口
    關於繼承的概念這裡同樣不再贅述,有興趣的話,可以閱讀 go 學習筆記之是否支持以及如何實現繼承.封裝和繼承都是在描述同類事物模型彼此共性,正如貓和狗都是動物,運用繼承的概念表示的話,貓和狗繼承自動物.貓和狗不僅具備各自特殊的屬性和行為,還具備一般動物的屬性和行為.
  • IBM高級架構師結合Java多線程和Socket,帶你實戰微服務架構
    面向服務的架構1996年,Gartner 公司首次提出了面向服務的架構(Service-Oriented Architecture, SOA)這一軟體設計思想。 其核心理念是將一個個的業務功能包裝成一個個的標準服務, 為這些服務定義良好的接口和服務契約,以便在需要的時候可以重用和水平伸縮。
  • Java語言課程教與學(36學時和48學時教學大綱)
    2學時(1)   面向對象問題求解的基本思想與OOP的內涵;對象與類      1學時(2)   封裝與數據隱藏;繼承;多態      1學時第3章  Java語言基礎       4學時(1)     標識符與數據類型;表達式與語句
  • 十種常見的架構模式概述
    摘自CSDN博主https://itman.blog.csdn.net架構模式是在給定上下文的軟體架構中,針對常發生問題的一種通用、復用的解決方案。架構模式類似於軟體設計模式,但是範疇更廣。        本文中,我將簡要的闡述如下10中常見架構模式的應用和優缺點。     1. 分層模式     2.
  • 程序進階:Java的抽象類和接口介紹
    在前面的文章中,老梁和大家回顧了Java的三大特性。在上一篇文章中老梁著重講解了Java的繼承,我們可以了解到繼承的基本概念。我們知道可以通過Java的繼承機制來派生出子類,原來的類叫做父類或者超類。類和抽象類的知識點老梁就給大家講到這裡,接下來咱們來講一下Java中的另一個重要概念,接口。
  • Java語言程序設計教與學(32和48學時教學大綱)
    第1章  緒論        2學時(1)JAVA起源與JAVA的特徵       1學時(2)JAVA技術體系;JAVA虛擬機與運行平臺      1學時第2章  面向對象程序設計基本概念     2學時(1)面向對象問題求解的基本思想與OOP的內涵;對象與類      1學時(2)封裝與數據隱藏;繼承;多態
  • 微服務架構設計實踐總結和思考
    本文談談在微服務架構設計中的一些實踐和思考。對於SOA和微服務,我前面很多文章都進行了詳細的闡述,今天這篇文章重點還是放在一些架構設計和實踐的一些關鍵點思考上面。同時考慮和SOA,中臺思想的融合,考慮到API接口的復用性,進一步對單個微服務也進行了前後端分離開發。從單微服務的概念來說,微服務不是指具體的Http API接口服務,而是指拆分後的微服務模塊,因此微服務可以理解為:拆分後DB+微服務模塊+API接口提供。
  • 軟體項目實訓及課程設計指導——可擴展和可重用是架構設計的目標
    軟體項目實訓及課程設計指導——可擴展性和可重用性是面向對象架構設計的主要目標1、什麼是合理的軟體應用系統的系統架構設計軟體應用系統的設計人員經常會陷入一種困惑,面向對象系統架構設計結果的評價標準是什麼?