寫(xiě)了10年JAVA代碼,為何還是給人一種亂糟糟的感覺(jué)?

接觸過(guò)不少號(hào)稱(chēng)寫(xiě)了10多年代碼的程序員,可經(jīng)常還是會(huì)發(fā)現(xiàn)他們的代碼給人一種亂糟糟的感覺(jué),那么如何才能寫(xiě)出讓同事感覺(jué)不那么亂的代碼呢?

陸川ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場(chǎng)景,ssl證書(shū)未來(lái)市場(chǎng)廣闊!成為創(chuàng)新互聯(lián)的ssl證書(shū)銷(xiāo)售渠道,可以享受市場(chǎng)價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18980820575(備注:SSL證書(shū)合作)期待與您的合作!

一、為什么要寫(xiě)這篇文章

在開(kāi)篇之前先說(shuō)明下為什么要寫(xiě)這篇文章?在Java的世界里MVC軟件架構(gòu)模式絕對(duì)是經(jīng)典的存在(PS:MVC是一種軟件架構(gòu)方式并不只有Java有),如果你是在最近十年前后進(jìn)入Java的編程世界,那么你會(huì)發(fā)現(xiàn)自己這些年似乎從來(lái)沒(méi)有逃離MVC架構(gòu)模式的牢籠,只不過(guò)換著使用了不同的MVC框架,如早期的Struts1、Struts2以及現(xiàn)在幾乎一統(tǒng)江湖的Spring MVC(少數(shù)自行封裝MVC框架的公司除外)。

而隨著互聯(lián)網(wǎng)技術(shù)的發(fā)展,特別是Ajax等富客戶(hù)端技術(shù)的發(fā)展,前端技術(shù)逐步形成了一套體系,并且逐步從后端代碼(如JSP)中剝離出來(lái),從而形成了現(xiàn)在普遍流行的前后端分離模式(這也是一段時(shí)間內(nèi)為什么前端工程師會(huì)出現(xiàn)大量需求的原因),而這也對(duì)傳統(tǒng)的MVC模式產(chǎn)生了一點(diǎn)小的改變,因?yàn)楝F(xiàn)在基于Java的后端服務(wù)中很少會(huì)有大量處理復(fù)雜界面邏輯的代碼出現(xiàn),因此MVC中的V(View)這一層就逐步被各類(lèi)前端技術(shù)所替代,如AngularJS、React等。

所以現(xiàn)在的Java服務(wù)端絕大部分情況下只是在處理M(Model)+C(Controller)的邏輯,而從概念上來(lái)看,好像Model代表的就是數(shù)據(jù)模型、而C則是一種控制層邏輯,所以很多人(甚至包括一些寫(xiě)了很多年Java代碼的人)有時(shí)候都會(huì)被這個(gè)概念所迷惑而在Model和Controller層之間搖擺不定,在這里我們需要明確MVC模式中的M不僅僅代表的是數(shù)據(jù)模型,而是包括了數(shù)據(jù)模型之內(nèi)的所有業(yè)務(wù)邏輯相關(guān)的代碼,而C則是比較輕的,它被賦予只有處理輸入/輸出參數(shù)以及對(duì)該請(qǐng)求進(jìn)行邏輯流程控制的職能,如果你的代碼中對(duì)Controller層有過(guò)重的邏輯代碼侵入,要知道這是不符合MVC架構(gòu)規(guī)范的!

在MVC架構(gòu)定義中,由于M代表了所有業(yè)務(wù)邏輯相關(guān)的代碼,所以M是要重點(diǎn)設(shè)計(jì)和規(guī)范的,其代碼的結(jié)構(gòu)和規(guī)范直接決定了軟件的可維護(hù)性及質(zhì)量,從本質(zhì)上來(lái)說(shuō)就是如何進(jìn)行"代碼結(jié)構(gòu)+軟件設(shè)計(jì)原則+設(shè)計(jì)模式"的組合運(yùn)用。當(dāng)然上面只是一句話,而其內(nèi)涵則是一件非??简?yàn)編程水平的事情。關(guān)于軟件設(shè)計(jì)原則+設(shè)計(jì)模式的內(nèi)容非常豐富也需要時(shí)間+經(jīng)驗(yàn)的積累!而代碼結(jié)構(gòu)則是可以通過(guò)一定規(guī)范進(jìn)行約定,結(jié)合Spring MVC框架至少我們可以寫(xiě)出層次結(jié)構(gòu)盡可能一致的代碼!

二、應(yīng)用分層怎么搞?

事實(shí)上關(guān)于Java如何規(guī)范開(kāi)發(fā)的問(wèn)題,不同公司的規(guī)范略有不同,不過(guò)作為國(guó)內(nèi)Java語(yǔ)言應(yīng)用最為廣泛的公司——阿里巴巴發(fā)布的《阿里巴巴Java開(kāi)發(fā)手冊(cè)》中對(duì)應(yīng)用的分層結(jié)構(gòu)已經(jīng)做了比較合理的劃分!這里作者并不想標(biāo)新立異,只是在此基礎(chǔ)上做更為詳細(xì)的解釋和說(shuō)明從而讓使用Spring MVC框架的同學(xué)能夠更好地明確其分層的對(duì)應(yīng)關(guān)系!

分層結(jié)構(gòu)

以下分層結(jié)構(gòu)基于Spring MVC框架,總體上與阿里巴巴開(kāi)發(fā)手冊(cè)應(yīng)用分層方式一致,分層結(jié)構(gòu)示意圖如下:

寫(xiě)了10年JAVA代碼,為何還是給人一種亂糟糟的感覺(jué)?

在基于Spring MVC框架的開(kāi)發(fā)中,Controller層作為服務(wù)的入口主要承擔(dān)接收和轉(zhuǎn)換由終端層或者其他服務(wù)發(fā)送的網(wǎng)絡(luò)請(qǐng)求,并將其轉(zhuǎn)化為Java數(shù)據(jù)對(duì)象,然后對(duì)數(shù)據(jù)對(duì)象進(jìn)行參數(shù)合法性校驗(yàn)(如字段長(zhǎng)度、類(lèi)型、數(shù)值的合法性等等)。之后通過(guò)在Controller依賴(lài)注入對(duì)應(yīng)Service層服務(wù)接口,并進(jìn)行業(yè)務(wù)邏輯層方法調(diào)用,如果業(yè)務(wù)邏輯并不復(fù)雜(是否復(fù)雜判斷標(biāo)準(zhǔn)可通過(guò)方法代碼行數(shù)、條件邏輯復(fù)雜度以及站在旁者角度看看是否便于維護(hù)等指標(biāo)進(jìn)行判斷)那么可以直接操作數(shù)據(jù)庫(kù)持久層完成業(yè)務(wù)邏輯;而如果Service層方法寫(xiě)著寫(xiě)著發(fā)現(xiàn)非常的多,邏輯條件也比較多,并且每個(gè)條件所需要處理的代碼量超過(guò)一定的規(guī)模,那么此時(shí)你就要考慮是否需要要對(duì)該方法進(jìn)行優(yōu)化了!

而關(guān)于優(yōu)化的方式依據(jù)邏輯的復(fù)雜程度可以做不同等級(jí)的拆分,例如簡(jiǎn)單點(diǎn)可以拆分一個(gè)私有方法處理該方法中的某一部分邏輯,從而減少主業(yè)務(wù)方法的代碼量。而如果該業(yè)務(wù)層方法后面對(duì)應(yīng)的是一個(gè)龐大的邏輯,例如在交易支付系統(tǒng)中,Controller層定義了一個(gè)支付的入口服務(wù),而進(jìn)入Service層方法后根據(jù)不同的業(yè)務(wù)接入方、不同的支付方式及支付渠道,都需要進(jìn)行大量不同邏輯的處理,那么此時(shí)就需要考慮對(duì)這些不同場(chǎng)景的業(yè)務(wù)邏輯進(jìn)行類(lèi)級(jí)別的拆分,如通過(guò)工廠模式拆分不同的支付渠道處理類(lèi)邏輯,而對(duì)于公共的處理邏輯則可以通過(guò)抽象類(lèi)定義抽象方法進(jìn)行抽象。例如私有方法拆分代碼示例:

@Override
public SearchCouponNameBO searchCouponNameList(SearchCouponNameDTO searchCouponNameDTO) {
    SearchCouponNameBO searchCouponNameBO = SearchCouponNameBO.builder().total(0).build();
    SearchResult searchResult;
    try {
        BoolQueryCondition boolQueryCondition = searchCouponNameListConditionBuild(searchCouponNameDTO);
        SearchBuilderConstructor searchBuilderConstructor = new SearchBuilderConstructor(boolQueryCondition);
        searchBuilderConstructor.addFieldSort("id", SortOrderEnum.DESC);
        searchBuilderConstructor.setFrom(searchCouponNameDTO.getOffset());
        searchBuilderConstructor.setSize(searchCouponNameDTO.getLimit());
        searchResult = salesCouponEsMapper.selectCouponNameByCondition(searchBuilderConstructor);
    } catch (Exception e) {
        throw new SalesCouponNameException(SalesCouponNameErrorCode.COUPON_NAME_ES_QUERY_ERROR.getCode(),
                SalesCouponNameErrorCode.COUPON_NAME_ES_QUERY_ERROR.getMessage(),
                searchCouponNameDTO);
    }
    if (searchResult != null && searchResult.getHits().getHits().length > 0) {
        List<Integer> idList = getIdListFromEsSearchResult(searchResult);
        List<SalesCouponNamePO> salesCouponNamePOList = salesCouponNameMapper.selectByIdList(idList);
        List<SalesCouponNameBO> couponNameBOList = SalesCouponNameConvert.INSTANCE
                .convertCouponNameBOList(salesCouponNamePOList);
        searchCouponNameBO.setList(couponNameBOList);
        searchCouponNameBO.setTotal((int) searchResult.getTotalHits());
    }
    return searchCouponNameBO;
}

在該Service入口方法中,需要根據(jù)從ES查詢(xún)的分頁(yè)ID去真實(shí)的MySQL中進(jìn)行數(shù)據(jù)獲?。‥S數(shù)據(jù)存儲(chǔ)不全,只是為了進(jìn)行優(yōu)化性能將分頁(yè)邏輯放入ES),而在處理ES數(shù)據(jù)時(shí),需要從ES數(shù)據(jù)結(jié)果集中抽象ID列表,對(duì)于這部分邏輯出于代碼量的考慮,這里我們抽象一個(gè)Service層私有方法,如:

private List<Integer> getIdListFromEsSearchResult(SearchResult searchResult) {
    SearchHit[] searchHits = searchResult.getHits().getHits();
    List<Integer> idList = Arrays.asList(searchHits).stream().map(SearchHit::getSourceAsMap)
            .map(o -> Integer.parseInt(String.valueOf(o.get("id"))))
            .collect(Collectors.toList());
    return idList;
}

以上代碼示例,本質(zhì)上是一種最簡(jiǎn)單的方法抽象(別的語(yǔ)言叫函數(shù)),如果在代碼量略大,但是邏輯本身復(fù)雜度還不是特別高的情況下,這種方式是最常用的!也是在你不知道怎么拆分,讓代碼不那么難以維護(hù)的一種非常有效的手段。

而工廠+責(zé)任鏈等也是業(yè)務(wù)層拆分常用的手段,此時(shí)需要基于Service層業(yè)務(wù)入口方法進(jìn)行代碼結(jié)構(gòu)的二次拆分,在分層結(jié)構(gòu)上這部分介于Service層和Dao層之間的代碼稱(chēng)之為通用業(yè)務(wù)處理層(Manager)。關(guān)于這部分由于可以發(fā)揮空間非常大,很難有一套標(biāo)準(zhǔn)的答案,但作為一名優(yōu)秀的程序設(shè)計(jì)者要時(shí)刻有抽象的思維,不管拆分得是否足夠合理,至少要讓你的代碼不至于過(guò)于臃腫!這里我們將Service層拆分層次定義為以下三個(gè)等級(jí):

  • 等級(jí)1:私有方法拆分;
  • 等級(jí)2:工廠+責(zé)任鏈運(yùn)用(有效的類(lèi)的拆分);
  • 等級(jí)3:高級(jí)設(shè)計(jì)模式(優(yōu)雅的類(lèi)的拆分);
分層領(lǐng)域模型約定

聊完分層結(jié)構(gòu)接下來(lái)我們說(shuō)一下分層領(lǐng)域數(shù)據(jù)模型的約定,注意這里的分層領(lǐng)域并不是指“DDD(領(lǐng)域驅(qū)動(dòng)設(shè)計(jì))模式”,而是對(duì)以上分層結(jié)構(gòu)中各層之間交互數(shù)據(jù)對(duì)象的定義約定。在上述分層結(jié)構(gòu)圖中已經(jīng)標(biāo)識(shí)了DTO、BO、PO的使用范圍(本規(guī)范只約定三種領(lǐng)域?qū)ο?,事?shí)上已經(jīng)足夠,并不需要搞的太復(fù)雜)。具體如下:

寫(xiě)了10年JAVA代碼,為何還是給人一種亂糟糟的感覺(jué)?

在Controller層接收網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)后,由于Controller層并不需要處理額外的邏輯,所以大部分情況下直接將DTO對(duì)象傳送給Service層;而Service層如果邏輯不復(fù)雜只是需要根據(jù)DTO的數(shù)據(jù)進(jìn)行數(shù)據(jù)庫(kù)操作,那么此時(shí)根據(jù)需要將DTO轉(zhuǎn)換為PO進(jìn)行操作,完成后由于大部分場(chǎng)景下Service的輸出參數(shù)與輸入DTO對(duì)象都存在差異,因此為了區(qū)分我們將Service層的輸出數(shù)據(jù)對(duì)象統(tǒng)一定義為BO。

而Service層拆分時(shí)對(duì)于Manager層方法的輸入/輸出對(duì)象則統(tǒng)一為BO,包括Manager層操作第三方數(shù)據(jù)接口的數(shù)據(jù)對(duì)象轉(zhuǎn)換也統(tǒng)一為BO。以上劃分并沒(méi)有什么特別的強(qiáng)制約定,而過(guò)分人為的去揣摩其含義本質(zhì)上也沒(méi)什么意義,只是大家共同遵守一個(gè)約定,這樣代碼風(fēng)格看起來(lái)會(huì)更加統(tǒng)一一點(diǎn)。

三、如何保持代碼的簡(jiǎn)潔性

作為一名對(duì)代碼有追求的程序員,能少些一行代碼就絕對(duì)不要啰嗦,而Java豐富的開(kāi)源生態(tài)體系也給了我們這種懶惰很多便利,所以在編程的過(guò)程中其實(shí)是有很多工具可以幫助節(jié)省代碼的。這里給大家分別介紹三種方式:

MapStruct

在前面介紹的分層結(jié)構(gòu)中,無(wú)論是DTO到BO,還是BO到PO亦或BO到BO,都會(huì)有很多的數(shù)據(jù)對(duì)象轉(zhuǎn)換的邏輯,傳統(tǒng)的方法是需要通過(guò)一堆Setter方法來(lái)完成的,而高級(jí)一點(diǎn)的lombok包提供的@Builder注解也是需要你寫(xiě)一堆".build()"來(lái)完成數(shù)據(jù)的轉(zhuǎn)換,這樣的代碼寫(xiě)到Service層中顯然很浪費(fèi)很多代碼行,而MapStruct是一種更優(yōu)雅的完成這件事的工具,使用方法如下:

項(xiàng)目pom.xml中引入依賴(lài):

<!--MapStruct Java實(shí)體映射工具依賴(lài)-->
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-jdk8</artifactId>
    <version>1.3.1.Final</version>
</dependency>
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.3.1.Final</version>
</dependency>

也需要在pom.xml引入一下Maven插件:

<!--提供給MapStruct使用 -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
</plugin>

之后編寫(xiě)數(shù)據(jù)對(duì)象映射轉(zhuǎn)換接口:

package com.mafengwo.sales.sp.coupon.convert;

import com.mafengwo.sales.sp.coupon.client.bo.SalesCouponChannelBO;
import com.mafengwo.sales.sp.coupon.client.dto.SalesCouponChannelsDTO;
import com.mafengwo.sales.sp.coupon.dao.model.SalesCouponChannelsPO;
import java.util.List;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;

/**
 * @author qiaojiang
 */
@Mapper
public interface SalesCouponChannelsConvert {

    SalesCouponChannelsConvert INSTANCE = Mappers.getMapper(SalesCouponChannelsConvert.class);

    @Mappings({
            @Mapping(target = "flag", expression = "java(java.lang.Integer.valueOf(\"0\"))"),
            @Mapping(target = "ctime", expression = "java(com.mafengwo.sales.sp.coupon.util.DateUtils.getCurrentTimestamp())"),
            @Mapping(target = "mtime", expression = "java(com.mafengwo.sales.sp.coupon.util.DateUtils.getCurrentTimestamp())")
    })
    SalesCouponChannelsPO convertSalesCouponChannelsPO(SalesCouponChannelsDTO salesCouponChannelsDTO);

    @Mappings({})
    List<SalesCouponChannelBO> convertCouponChannelBOList(List<SalesCouponChannelsPO> salesCouponChannelsPO);
}

以上方法的入?yún)樵磾?shù)據(jù)對(duì)象,而返回對(duì)象則為目標(biāo)數(shù)據(jù)對(duì)象,如果兩個(gè)對(duì)象的字段名稱(chēng)完成一致,那么其實(shí)是不需要進(jìn)行任何單獨(dú)映射的,直接 @Mappings({})即可;而如果映射對(duì)象之間字段名稱(chēng)有差異則可以通過(guò)@Mappings({@Mapping(target = "ctime", source = "createTime")})進(jìn)行指定映射。而在業(yè)務(wù)層方法具體操作時(shí)使用方法如下:

//實(shí)體數(shù)據(jù)轉(zhuǎn)換
SalesCouponChannelsPO salesCouponChannelsPO = SalesCouponChannelsConvert.INSTANCE
        .convertSalesCouponChannelsPO(salesCouponChannelsDTO);

這樣對(duì)象數(shù)據(jù)之間的拷貝將變得非常容易,從某種層面上看無(wú)論代碼層次結(jié)構(gòu)多么繞,至少數(shù)據(jù)對(duì)象之間的拷貝將不再是一件麻煩的事!

lambada表達(dá)式

在Java8種提供了lambada表達(dá)式,在Java8中如果操作List相關(guān)數(shù)據(jù)結(jié)構(gòu),如果能夠使用lambada表達(dá)式也可以省一些代碼,例如:

private List<Integer> getIdListFromEsSearchResult(SearchResult searchResult) {
    SearchHit[] searchHits = searchResult.getHits().getHits();
    List<Integer> idList = Arrays.asList(searchHits).stream().map(SearchHit::getSourceAsMap)
            .map(o -> Integer.parseInt(String.valueOf(o.get("id"))))
            .collect(Collectors.toList());
    return idList;
}

有關(guān)lambada表達(dá)式更多的用法,大家有時(shí)間可以多看看相關(guān)語(yǔ)法知識(shí),這里就不再贅述!

tk.mybatis

在使用Mybatis框架作為數(shù)據(jù)庫(kù)開(kāi)發(fā)框架時(shí),相比較于Hibernate或其他JPA框架,Mybatis具有較強(qiáng)的對(duì)原生SQL的支持能力,因而會(huì)顯得比較靈活。但在大部分互聯(lián)網(wǎng)系統(tǒng)中,對(duì)數(shù)據(jù)庫(kù)的操作很多時(shí)候都是單表的操作,在這種情況下使用Mybatis也需要在Mapper代碼和映射.xml文件中編寫(xiě)大量的SQL,而這些單表SQL本質(zhì)上大同小異,完全可以通用化。

因此在Mybatis領(lǐng)域?yàn)榱藴p少開(kāi)發(fā)量很多項(xiàng)目會(huì)使用mybatis-generator插件生成一份完整的映射代碼,但是這樣的方式也會(huì)增加大量的無(wú)用代碼,看起來(lái)并不是那么的簡(jiǎn)潔。而tk.mybatis則是考慮到了這個(gè)問(wèn)題,可以兼顧對(duì)單表操作的便捷性(不需要再寫(xiě)額外的代碼)、多表聯(lián)合查詢(xún)的靈活性以及代碼的簡(jiǎn)潔性。具體用法如下:

項(xiàng)目pom.xml文件引入相關(guān)依賴(lài):

<!--Mybatis通用Mapper集成-->
<dependency>
    <groupId>tk.mybatis</groupId>
    <artifactId>mapper-spring-boot-starter</artifactId>
    <version>2.1.3</version>
    <exclusions>
        <exclusion>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>tk.mybatis</groupId>
    <artifactId>mapper</artifactId>
    <version>4.1.3</version>
</dependency>

主類(lèi)@MapperScan注解換成tk.mybatis的:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.elasticsearch.ElasticSearchRestHealthIndicatorAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
//不要使用Mybatis原生注解,用tk.mybatis的
import tk.mybatis.spring.annotation.MapperScan;

import java.util.Date;

@SpringBootApplication(exclude = {ElasticSearchRestHealthIndicatorAutoConfiguration.class})
@ServletComponentScan
@EnableDiscoveryClient
@EnableWebMvc
@MonitorEnableAutoConfiguration
@MapperScan("com.mafengwo.sales.sp.coupon.dao.mapper")
@EnableTransactionManagement
public class SpCouponApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpCouponApplication.class, args);
    }
}

編寫(xiě)映射接口,單表操作將不再需要額外定義操作方法及映射SQL代碼,而是可以直接用tk.mybatis提供的通用方法,代碼如下:

import com.mafengwo.sales.sp.coupon.dao.model.CouponNameScopeRelationPO;
import org.springframework.stereotype.Repository;
import tk.mybatis.mapper.common.Mapper;

@Repository
public interface CouponNameScopeRelationMapper extends Mapper<CouponNameScopeRelationPO> {

}

而在Mybatis SQL映射文件*.xml中單表也只需要定義簡(jiǎn)單的字段映射即可,而不在需要定義通篇的SQL代碼了,如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.mafengwo.sales.sp.coupon.dao.mapper.SalesCouponChannelsMapper">
    <resultMap id="BaseResultMap" type="com.mafengwo.sales.sp.coupon.dao.model.SalesCouponChannelsPO">
        <id column="ID" property="id" jdbcType="INTEGER"/>
        <result column="NAME" property="name" jdbcType="VARCHAR"/>
        <result column="DESC" property="desc" jdbcType="VARCHAR"/>
        <result column="ADMIN_UID" property="adminUid" jdbcType="INTEGER"/>
        <result column="FLAG" property="flag" jdbcType="INTEGER"/>
        <result column="CTIME" property="ctime" jdbcType="TIMESTAMP"/>
        <result column="MTIME" property="mtime" jdbcType="TIMESTAMP"/>
        <result column="SCENEID" property="sceneId" jdbcType="INTEGER"/>
    </resultMap>
</mapper>

除以上工具外,在實(shí)際的開(kāi)發(fā)過(guò)程中還有很多開(kāi)源或通過(guò)自定義組件的方式能夠讓代碼寫(xiě)的更簡(jiǎn)潔,大家可以保持探索!

四、Java程序設(shè)計(jì)原則與設(shè)計(jì)模式

構(gòu)建復(fù)雜的軟件系統(tǒng)只有遵循一定的設(shè)計(jì)原則并合適地運(yùn)用相應(yīng)地設(shè)計(jì)模式,這樣的代碼才不至于在復(fù)雜的邏輯中迷失方向。關(guān)于設(shè)計(jì)原則及設(shè)計(jì)模式的話題是一個(gè)需要時(shí)間打磨和反復(fù)歷練的修行,因此這里只是為大家簡(jiǎn)單陳列,在Java程序設(shè)計(jì)時(shí)應(yīng)該遵循的一些原則以及可用的設(shè)計(jì)原則,做到心中有劍!

設(shè)計(jì)原則

單一職責(zé)(一個(gè)蘿卜一個(gè)坑)、里氏替換(繼承復(fù)用)、依賴(lài)倒置(面向接口編程)、接口隔離(高內(nèi)聚、低耦合)、迪米特法則(降低類(lèi)與類(lèi)之間的耦合)、開(kāi)閉原則(對(duì)擴(kuò)展開(kāi)發(fā)、對(duì)修改關(guān)閉)。

設(shè)計(jì)模式

在Java領(lǐng)域,大概有23種設(shè)計(jì)模式,它們分別是:

  • 創(chuàng)建型模式:?jiǎn)卫J健⒊橄蠊S模式、建造者模式、工廠模式、原型模式
  • 結(jié)構(gòu)型模式:適配器模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式
  • 行為型模式:模板方法模式、命令模式、迭代器模式、觀察者模式、中介者模式、備忘錄模式、解釋器模式、狀態(tài)模式、策略模式

以上這些模式或多或少在我們?nèi)粘5木幊讨卸紩?huì)見(jiàn)到或者聽(tīng)過(guò),但在平時(shí)能夠用到的卻并不多,很多原因在于目前Java領(lǐng)域的開(kāi)發(fā)框架如Spring已經(jīng)給我們做了很多的限定,而在大部分互聯(lián)網(wǎng)系統(tǒng)中,編程模式又很固定。在多數(shù)情況下,工廠模式的運(yùn)用就能搞定大多數(shù)業(yè)務(wù)編程場(chǎng)景,因此很多模式只有在很多中間件系統(tǒng)等基礎(chǔ)軟件中被使用得比較多。通過(guò)羅列上述設(shè)計(jì)模式,并不是要大家為了設(shè)計(jì)而生硬的使用設(shè)計(jì)模式,而是要努力向著“心中有丘壑,眉目作山河”目標(biāo)境界前進(jìn)!只有這樣才能不至于日復(fù)一日的碼磚生涯中,迷失自我,失去方向!

后記

隨著時(shí)光的流逝,越來(lái)越多的程序員步入中年,寫(xiě)了10多年代碼的人也越來(lái)越多,而行業(yè)的發(fā)展卻在走下坡路,種種因素讓越來(lái)越多的人感到焦慮!個(gè)人覺(jué)得作為一名程序員,我們的核心能力還在于代碼,因此在日復(fù)一日的碼磚生涯中不斷修煉自己的代碼能力才是關(guān)鍵!否則可能就會(huì)出現(xiàn)被年輕人鄙視了!

網(wǎng)頁(yè)題目:寫(xiě)了10年JAVA代碼,為何還是給人一種亂糟糟的感覺(jué)?
文章分享:http://muchs.cn/article40/ipjcho.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計(jì)Google、企業(yè)網(wǎng)站制作企業(yè)建站、App設(shè)計(jì)域名注冊(cè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

營(yíng)銷(xiāo)型網(wǎng)站建設(shè)