39丨 運(yùn)用學(xué)過的設(shè)計(jì)原則和思想完善之前講的性能計(jì)數(shù)器項(xiàng)目上_W_第1頁
39丨 運(yùn)用學(xué)過的設(shè)計(jì)原則和思想完善之前講的性能計(jì)數(shù)器項(xiàng)目上_W_第2頁
39丨 運(yùn)用學(xué)過的設(shè)計(jì)原則和思想完善之前講的性能計(jì)數(shù)器項(xiàng)目上_W_第3頁
39丨 運(yùn)用學(xué)過的設(shè)計(jì)原則和思想完善之前講的性能計(jì)數(shù)器項(xiàng)目上_W_第4頁
39丨 運(yùn)用學(xué)過的設(shè)計(jì)原則和思想完善之前講的性能計(jì)數(shù)器項(xiàng)目上_W_第5頁
已閱讀5頁,還剩11頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

1、下載APP39 | 運(yùn)用學(xué)過的設(shè)計(jì)原則和思想完善之前講的性能計(jì)數(shù)器項(xiàng)目(上)2020-01-31 王爭設(shè)計(jì)模式之美進(jìn)入課程講述:馮永吉時(shí)長 12:29 大小 10.01M在第 25 節(jié)、第 26 節(jié)中,我們講了如何對(duì)一個(gè)性能計(jì)數(shù)器框架進(jìn)行分析、設(shè)計(jì)與實(shí)現(xiàn),并且實(shí)踐了之前學(xué)過的一些設(shè)計(jì)原則和設(shè)計(jì)思想。當(dāng)時(shí)我們提到,小步快跑、逐步迭代是一種非常實(shí)用的開發(fā)模式。所以,針對(duì)這個(gè)框架的開發(fā),我們分多個(gè)版本來逐步完善。在第 25、26 節(jié)課中,我們實(shí)現(xiàn)了框架的第一個(gè)版本,它只包含最基本的一些功能,在設(shè)計(jì)與實(shí)現(xiàn)上還有很多不足。所以,接下來,我會(huì)針對(duì)這些不足,繼續(xù)迭 發(fā)兩個(gè)版本:版本2 和版本 3,分別對(duì)應(yīng)第

2、 39 節(jié)和第 40 節(jié)的內(nèi)容。在版本 2 中,我們會(huì)利用之前學(xué)過的重構(gòu)方法,對(duì)版本 1 的設(shè)計(jì)與實(shí)現(xiàn)進(jìn)行重構(gòu),解決版本 1 存在的設(shè)計(jì)問題,讓它滿足之前學(xué)過的設(shè)計(jì)原則、思想、編程規(guī)范。在版本 3 中,我們?cè)賹?duì)版本 2 進(jìn)行迭代,并且完善框架的功能和非功能需求,讓其滿足第 25 節(jié)課中羅列的所有需求。話不多說,讓我們正式開始版本 2 的設(shè)計(jì)與實(shí)現(xiàn)吧!回顧版本 1 的設(shè)計(jì)與實(shí)現(xiàn)首先,讓我們一塊回顧一下版本 1 的設(shè)計(jì)與實(shí)現(xiàn)。當(dāng)然,如果時(shí)間充足,你最好能再重新看一下第 25、26 節(jié)的內(nèi)容。在版本 1 中,整個(gè)框架的代碼被劃分為下面這幾個(gè)類。MetricsCollector:負(fù)責(zé)打點(diǎn)采集原始數(shù)據(jù),

3、包括記錄每次接口請(qǐng)求的響應(yīng)時(shí)間和請(qǐng)求時(shí) 間戳,并調(diào)用 MetricsStorage 提供的接口來存儲(chǔ)這些原始數(shù)據(jù)。MetricsStorage 和 RedisMetricsStorage:負(fù)責(zé)原始數(shù)據(jù)的存儲(chǔ)和讀取。Aggregator:是一個(gè)工具類,負(fù)責(zé)各種統(tǒng)計(jì)數(shù)據(jù)的計(jì)算,比如響應(yīng)時(shí)間的最大值、最小值、平均值、百分位值、接口訪問次數(shù)、tps。ConsoleReporter 和 EmailReporter:相當(dāng)于一個(gè)上帝類(God Class),定時(shí)根據(jù)給定的時(shí)間區(qū)間,從數(shù)據(jù)庫中取出數(shù)據(jù),借助 Aggregator 類完成統(tǒng)計(jì)工作,并將統(tǒng)計(jì)結(jié)果輸出到相應(yīng)的終端,比如命令行、郵件。MetricCo

4、llector、MetricsStorage、RedisMetricsStorage 的設(shè)計(jì)與實(shí)現(xiàn)比較簡單,不是版本 2 重構(gòu)的重點(diǎn)。今天,我們重點(diǎn)來看一下 Aggregator 和 ConsoleReporter、EmailReporter 這幾個(gè)類。我們先來看一下 Aggregator 類存在的問題。Aggregator 類里面只有一個(gè)靜態(tài)函數(shù),有 50 行左右的代碼量,負(fù)責(zé)各種統(tǒng)計(jì)數(shù)據(jù)的計(jì)算。當(dāng)要添加新的統(tǒng)計(jì)功能的時(shí)候,我們需要修改 aggregate() 函數(shù)代碼。一旦越來越多的統(tǒng)計(jì)功能添加進(jìn)來之后,這個(gè)函數(shù)的代碼量會(huì)持續(xù)增加,可讀性、可維護(hù)性就變差了。因此,我們需要在版本 2 中對(duì)其

5、進(jìn)行重構(gòu)。復(fù)制代碼1 public class Aggregator 2 public static RequestStat aggregate(List requestInfos, long dur3 double maxRespTime = Double.MIN_VALUE;4 double minRespTime = Double.MAX_VALUE;5doubleavgRespTime = -1;6doublep999RespTime = -1;7doublep99RespTime = -1;8doublesumRespTime = 0;9 long count = 0;10 for

6、(RequestInfo requestInfo : requestInfos) 11+count;12double respTime = requestInfo.getResponseTime();13if (maxRespTime respTime) 17minRespTime = respTime;1819sumRespTime += respTime;2021if (count != 0) 22avgRespTime = sumRespTime / count;2324long tps = (long)(count / durationInMillis * 1000);25Collec

7、tions.sort(requestInfos, new Comparator() 26Override27public int compare(RequestInfo o1, RequestInfo o2) 28double diff = o1.getResponseTime() - o2.getResponseTime();29if (diff 0.0) 32return 1;33 else 34return 0;353637);3839if (count != 0) 40int idx999 = (int)(count * 0.999);41int idx99 = (int)(count

8、 * 0.99);42p999RespTime = requestInfos.get(idx999).getResponseTime();43p99RespTime = requestInfos.get(idx99).getResponseTime();4445RequestStat requestStat = new RequestStat();46requestStat.setMaxResponseTime(maxRespTime);47requestStat.setMinResponseTime(minRespTime);48requestStat.setAvgResponseTime(

9、avgRespTime);49requestStat.setP999ResponseTime(p999RespTime);50requestStat.setP99ResponseTime(p99RespTime);51requestStat.setCount(count);52requestStat.setTps(tps);53return requestStat;5455 5657 public class RequestStat 58 private double maxResponseTime;59 private double minResponseTime;60 private do

10、uble avgResponseTime;61 private double p999ResponseTime;62 private double p99ResponseTime;63 private long count;64 private long tps;65 /.省略getter/setter方法.66 我們?cè)賮砜匆幌?ConsoleReporter 和 EmailReporter 這兩個(gè)類存在的問題。ConsoleReporter 和 EmailReporter 兩個(gè)類中存在代碼重復(fù)問題。在這兩個(gè)類中,從數(shù)據(jù)庫中取數(shù)據(jù)、做統(tǒng)計(jì)的邏輯都是相同的,可以抽取出來復(fù)用,否則就違反了 DRY

11、 原則。整個(gè)類負(fù)責(zé)的事情比較多,不相干的邏輯糅合在里面,職責(zé)不夠單一。特別是顯示部分的代碼可能會(huì)比較復(fù)雜(比如 Email 的顯示方式),最好能將這部分顯示邏輯剝離出來,設(shè)計(jì)成一個(gè)獨(dú)立的類。除此之外,因?yàn)榇a中涉及線程操作,并且調(diào)用了 Aggregator 的靜態(tài)函數(shù),所以代碼的可測(cè)試性也有待提高。復(fù)制代碼1 public class ConsoleReporter 2 private MetricsStorage metricsStorage;3 private ScheduledExecutorService executor;45 public ConsoleReporter(Metri

12、csStorage metricsStorage) 6 this.metricsStorage = metricsStorage;7 this.executor = Executors.newSingleThreadScheduledExecutor();8910 public void startRepeatedReport(long periodInSeconds, long durationInSeconds11 executor.scheduleAtFixedRate(new Runnable() 12 Override13 public void run() 14 long dura

13、tionInMillis = durationInSeconds * 1000;15 long endTimeInMillis = System.currentTimeMillis();16 long startTimeInMillis = endTimeInMillis - durationInMillis;17 MapString, List requestInfos =18 metricsStorage.getRequestInfos(startTimeInMillis, endTimeInMil19 Map stats = new HashMap();20212223242526272

14、829for (Map.EntryString, List entry : requestInfos.entrySe String apiName = entry.getKey();List requestInfosPerApi = entry.getValue(); RequestStat requestStat = Aggregator.aggregate(requestInfosPerApi, d stats.put(apiName, requestStat);System.out.println(Time Span: + startTimeInMillis + , + endTime

15、Gson gson = new Gson();System.out.println(gson.toJson(stats);30313233 34, 0, periodInSeconds, TimeUnit.SECONDS);35 public class EmailReporter 36 private static final Long DAY_HOURS_IN_SECONDS = 86400L;3738 private MetricsStorage metricsStorage;39 private EmailSender emailSender;40 private List toAdd

16、resses = new ArrayList();4142 public EmailReporter(MetricsStorage metricsStorage) 43 this(metricsStorage, new EmailSender(/*省略參數(shù)*/);444546 public EmailReporter(MetricsStorage metricsStorage, EmailSender emailSender)47 this.metricsStorage = metricsStorage;48 this.emailSender = emailSender;495051 publ

17、ic void addToAddress(String address) 52 toAddresses.add(address);535455 public void startDailyReport() 56 Calendar calendar = Calendar.getInstance();57 calendar.add(Calendar.DATE, 1);58 calendar.set(Calendar.HOUR_OF_DAY, 0);59 calendar.set(Calendar.MINUTE,0);60 calendar.set(Calendar.SECOND,0);61 cal

18、endar.set(Calendar.MILLISECOND, 0);62 Date firstTime = calendar.getTime();63 Timer timer = new Timer();64 timer.schedule(new TimerTask() 65 Override66 public void run() 67 long durationInMillis = DAY_HOURS_IN_SECONDS * 1000;68 long endTimeInMillis = System.currentTimeMillis();69 long startTimeInMill

19、is = endTimeInMillis - durationInMillis;70 MapString, List requestInfos =71 metricsStorage.getRequestInfos(startTimeInMillis, endTimeInMil72 Map stats = new HashMap();73 for (Map.EntryString, List entry : requestInfos.entrySe74String apiName = entry.getKey();75List requestInfosPerApi = entry.getValu

20、e();76RequestStat requestStat = Aggregator.aggregate(requestInfosPerApi, d77stats.put(apiName, requestStat);7879/ TODO: 格式化為html格式,并且發(fā)送郵件8081, firstTime, DAY_HOURS_IN_SECONDS * 1000);828384針對(duì)版本 1 的問題進(jìn)行重構(gòu)Aggregator 類和 ConsoleReporter、EmailReporter 類主要負(fù)責(zé)統(tǒng)計(jì)顯示的工作。在第26 節(jié)中,我們提到,如果我們把統(tǒng)計(jì)顯示所要完成的功能邏輯細(xì)分一下,主要包含

21、下面 4點(diǎn):1. 根據(jù)給定的時(shí)間區(qū)間,從數(shù)據(jù)庫中拉取數(shù)據(jù);2. 根據(jù)原始數(shù)據(jù),計(jì)算得到統(tǒng)計(jì)數(shù)據(jù);3. 將統(tǒng)計(jì)數(shù)據(jù)顯示到終端(命令行或郵件);4. 定時(shí)觸發(fā)以上三個(gè)過程的執(zhí)行。之前的劃分方法是將所有的邏輯都放到 ConsoleReporter 和 EmailReporter 這兩個(gè)上帝類中,而 Aggregator 只是一個(gè)包含靜態(tài)方法的工具類。這樣的劃分方法存在前面提到的一些問題,我們需要對(duì)其進(jìn)行重新劃分。面向?qū)ο笤O(shè)計(jì)中的最后一步是組裝類并提供執(zhí)行入口,所以,組裝前三部分邏輯的上帝類是必須要有的。我們可以將上帝類做的很輕量級(jí),把核心邏輯都剝離出去,形成獨(dú)立的類,上帝類只負(fù)責(zé)組裝類和串聯(lián)執(zhí)行流程

22、。這樣做的好處是,代碼結(jié)構(gòu)更加清晰,底層核心邏輯更容易被復(fù)用。按照這個(gè)設(shè)計(jì)思路,具體的重構(gòu)工作包含以下 4 個(gè)方面。第 1 個(gè)邏輯:根據(jù)給定時(shí)間區(qū)間,從數(shù)據(jù)庫中拉取數(shù)據(jù)。這部分邏輯已經(jīng)被封裝在MetricsStorage 類中了,所以這部分不需要處理。第 2 個(gè)邏輯:根據(jù)原始數(shù)據(jù),計(jì)算得到統(tǒng)計(jì)數(shù)據(jù)。我們可以將這部分邏輯移動(dòng)到Aggregator 類中。這樣 Aggregator 類就不僅僅是只包含統(tǒng)計(jì)方法的工具類了。按照這個(gè)思路,重構(gòu)之后的代碼如下所示:1 public class Aggregator 2 public Map aggregate(復(fù)制代碼345678910111213Map

23、String, List requestInfos, long durationInMillis) Map requestStats = new HashMap();for (Map.EntryString, List entry : requestInfos.entrySet() String apiName = entry.getKey();List requestInfosPerApi = entry.getValue();RequestStat requestStat = doAggregate(requestInfosPerApi, durationInMill requestSta

24、ts.put(apiName, requestStat);return requestStats;141516171819202122232425262728293031323334353637383940 private RequestStat doAggregate(List requestInfos, long duratio List respTimes = new ArrayList();for (RequestInfo requestInfo : requestInfos) double respTime = requestInfo.getResponseTime(); respT

25、imes.add(respTime);RequestStat requestStat = new RequestStat(); requestStat.setMaxResponseTime(max(respTimes); requestStat.setMinResponseTime(min(respTimes); requestStat.setAvgResponseTime(avg(respTimes); requestStat.setP999ResponseTime(percentile999(respTimes); requestStat.setP99ResponseTime(percen

26、tile99(respTimes); requestStat.setCount(respTimes.size();requestStat.setTps(long) tps(respTimes.size(), durationInMillis/1000); return requestStat;/ 以下的函數(shù)的代碼實(shí)現(xiàn)均省略.private double max(List dataset) private double min(List dataset) private double avg(List dataset) private double tps(int count, double d

27、uration) private double percentile999(List dataset) private double percentile99(List dataset) private double percentile(List dataset, double ratio) 第 3 個(gè)邏輯:將統(tǒng)計(jì)數(shù)據(jù)顯示到終端。我們將這部分邏輯剝離出來,設(shè)計(jì)成兩個(gè)類: ConsoleViewer 類和 EmailViewer 類,分別負(fù)責(zé)將統(tǒng)計(jì)結(jié)果顯示到命令行和郵件中。具體的代碼實(shí)現(xiàn)如下所示:1 public interface StatViewer 復(fù)制代碼2 void output(M

28、ap requestStats, long startTimeInMillis, l3 45 public class ConsoleViewer implements StatViewer 6 public void output(7 Map requestStats, long startTimeInMillis, long8 System.out.println(Time Span: + startTimeInMillis + , + endTimeInMi9 Gson gson = new Gson();10 System.out.println(gson.toJson(request

29、Stats);1112 1314 public class EmailViewer implements StatViewer 15 private EmailSender emailSender;16 private List toAddresses = new ArrayList();1718 public EmailViewer() 19 this.emailSender = new EmailSender(/*省略參數(shù)*/);202122public EmailViewer(EmailSender emailSender) 23this.emailSender = emailSende

30、r;242526public void addToAddress(String address) 27toAddresses.add(address);282930public void output(31Map requestStats, long startTimeInMillis,long32/ format the requestStats to HTML style.33/ send it to email toAddresses.3435 第 4 個(gè)邏輯:組裝類并定時(shí)觸發(fā)執(zhí)行統(tǒng)計(jì)顯示。在將核心邏輯剝離出來之后,這個(gè)類的代碼變得更加簡潔、清晰,只負(fù)責(zé)組裝各個(gè)類(MetricsStor

31、age、Aggegrator、StatViewer)來完成整個(gè)工作流程。重構(gòu)之后的代碼如下所示:1 public class ConsoleReporter 2 private MetricsStorage metricsStorage;3 private Aggregator aggregator;4 private StatViewer viewer;5 private ScheduledExecutorService executor;6復(fù)制代碼7891011121314151617181920212223242526272829 30public ConsoleReporter(Met

32、ricsStorage metricsStorage, Aggregator aggregator, this.metricsStorage = metricsStorage;this.aggregator = aggregator; this.viewer = viewer;this.executor = Executors.newSingleThreadScheduledExecutor();public void startRepeatedReport(long periodInSeconds, long durationInSeconds executor.scheduleAtFixe

33、dRate(new Runnable() Overridepublic void run() long durationInMillis = durationInSeconds * 1000; long endTimeInMillis = System.currentTimeMillis();long startTimeInMillis = endTimeInMillis - durationInMillis; MapString, List requestInfos =metricsStorage.getRequestInfos(startTimeInMillis, endTimeInMil

34、 Map requestStats = aggregator.aggregate(requestIn viewer.output(requestStats, startTimeInMillis, endTimeInMillis);, 0L, periodInSeconds, TimeUnit.SECONDS);31 public class EmailReporter 32 private static final Long DAY_HOURS_IN_SECONDS = 86400L;3334 private MetricsStorage metricsStorage;35 private A

35、ggregator aggregator;36 private StatViewer viewer;3738 public EmailReporter(MetricsStorage metricsStorage, Aggregator aggregator, S39 this.metricsStorage = metricsStorage;40 this.aggregator = aggregator;41 this.viewer = viewer;424344 public void startDailyReport() 45 Calendar calendar = Calendar.get

36、Instance();46 calendar.add(Calendar.DATE, 1);47 calendar.set(Calendar.HOUR_OF_DAY, 0);48 calendar.set(Calendar.MINUTE,0);49 calendar.set(Calendar.SECOND,0);50 calendar.set(Calendar.MILLISECOND, 0);51 Date firstTime = calendar.getTime();52Timer timer = new Timer();53timer.schedule(new TimerTask() 54O

37、verride55public void run() 56long durationInMillis = DAY_HOURS_IN_SECONDS * 1000;57long endTimeInMillis = System.currentTimeMillis();58long startTimeInMillis = endTimeInMillis - durationInMillis;59MapString, List requestInfos =60metricsStorage.getRequestInfos(startTimeInMillis, endTimeInMil61Map sta

38、ts = aggregator.aggregate(requestInfos, du62viewer.output(stats, startTimeInMillis, endTimeInMillis);6364, firstTime, DAY_HOURS_IN_SECONDS * 1000);6566 經(jīng)過上面的重構(gòu)之后,我們現(xiàn)在再來看一下,現(xiàn)在框架該如何來使用。我們需要在應(yīng)用啟動(dòng)的時(shí)候,創(chuàng)建好 ConsoleReporter 對(duì)象,并且調(diào)用它的startRepeatedReport() 函數(shù),來啟動(dòng)定時(shí)統(tǒng)計(jì)并輸出數(shù)據(jù)到終端。同理,我們還需要?jiǎng)?chuàng)建好 EmailReporter 對(duì)象,并且調(diào)用它

39、的 startDailyReport() 函數(shù),來啟動(dòng)每日統(tǒng)計(jì)并輸出數(shù)據(jù)到制定郵件地址。我們通過 MetricsCollector 類來收集接口的訪問情況,這部分收集代碼會(huì)跟業(yè)務(wù)邏輯代碼耦合在一起,或者統(tǒng)一放到類似 Spring AOP 的切面中完成。具體 的使用代碼示例如下:復(fù)制代碼1 public class PerfCounterTest 2 public static void main(String args) 3 MetricsStorage storage = new RedisMetricsStorage();4 Aggregator aggregator = new Aggr

40、egator();56 / 定時(shí)觸發(fā)統(tǒng)計(jì)并將結(jié)果顯示到終端7 ConsoleViewer consoleViewer = new ConsoleViewer();8 ConsoleReporter consoleReporter = new ConsoleReporter(storage, aggregator,9 consoleReporter.startRepeatedReport(60, 60);1011 / 定時(shí)觸發(fā)統(tǒng)計(jì)并將結(jié)果輸出到郵件12 EmailViewer emailViewer = new EmailViewer();13 emailViewer.addToAddress(

41、);14 EmailReporter emailReporter = new EmailReporter(storage, aggregator, email15 emailReporter.startDailyReport();1617 / 收集接口訪問數(shù)據(jù)18 MetricsCollector collector = new MetricsCollector(storage);19 collector.recordRequest(new RequestInfo(register, 123, 10234);20 collector.recordRequest(

42、new RequestInfo(register, 223, 11234);21 collector.recordRequest(new RequestInfo(register, 323, 12334);22 collector.recordRequest(new RequestInfo(login, 23, 12434);23 collector.recordRequest(new RequestInfo(login, 1223, 14234);2425try 26Thread.sleep(100000);27 catch (InterruptedException e) 28e.prin

43、tStackTrace();293031 Review 版本 2 的設(shè)計(jì)與實(shí)現(xiàn)現(xiàn)在,我們 Review 一下,針對(duì)版本 1 重構(gòu)之后,版本 2 的設(shè)計(jì)與實(shí)現(xiàn)。重 構(gòu) 之 后 ,MetricsStorage 負(fù) 責(zé) 存 儲(chǔ) ,Aggregator 負(fù) 責(zé) 統(tǒng) 計(jì) , StatViewer(ConsoleViewer、EmailViewer)負(fù)責(zé)顯示,三個(gè)類各司其職。ConsoleReporter 和EmailReporter 負(fù)責(zé)組裝這三個(gè)類,將獲取原始數(shù)據(jù)、聚合統(tǒng)計(jì)、顯示統(tǒng)計(jì)結(jié)果到終端這三個(gè)階段的工作串聯(lián)起來,定時(shí)觸發(fā)執(zhí)行。除此之外,MetricsStorage、Aggregator、Stat

44、Viewer 三個(gè)類的設(shè)計(jì)也符合迪米特法則。它們只與跟自己有直接相關(guān)的數(shù)據(jù)進(jìn)行交互。MetricsStorage 輸出的是 RequestInfo 相關(guān)數(shù)據(jù)。Aggregator 類輸入的是 RequestInfo 數(shù)據(jù),輸出的是 RequestStat 數(shù)據(jù)。StatViewer 輸入的是 RequestStat 數(shù)據(jù)。針對(duì)版本 1 和版本 2,我畫了一張它們的類之間依賴關(guān)系的對(duì)比圖,如下所示。從圖中, 我們可以看出,重構(gòu)之后的代碼結(jié)構(gòu)更加清晰、有條理。這也印證了之前提到的:面向?qū)ο?設(shè)計(jì)和實(shí)現(xiàn)要做的事情,就是把合適的代碼放到合適的類中。剛剛我們分析了代碼的整體結(jié)構(gòu)和依賴關(guān)系,我們現(xiàn)在再來具

45、體看每個(gè)類的設(shè)計(jì)。Aggregator 類從一個(gè)只包含一個(gè)靜態(tài)函數(shù)的工具類,變成了一個(gè)普通的聚合統(tǒng)計(jì)類。現(xiàn)在, 我們可以通過依賴注入的方式,將其組裝進(jìn) ConsoleReporter 和 EmailReporter 類中, 這樣就更加容易編寫單元測(cè)試。Aggregator 類在重構(gòu)前,所有的邏輯都集中在 aggregate() 函數(shù)內(nèi),代碼行數(shù)較多,代碼的可讀性和可維護(hù)性較差。在重構(gòu)之后,我們將每個(gè)統(tǒng)計(jì)邏輯拆分成獨(dú)立的函數(shù), aggregate() 函數(shù)變得比較單薄,可讀性提高了。盡管我們要添加新的統(tǒng)計(jì)功能,還是要修改 aggregate() 函數(shù),但現(xiàn)在的 aggregate() 函數(shù)代碼行

46、數(shù)很少,結(jié)構(gòu)非常清晰,修改起來更加容易,可維護(hù)性提高。目前來看,Aggregator 的設(shè)計(jì)還算合理。但是,如果隨著更多的統(tǒng)計(jì)功能的加入, Aggregator 類的代碼會(huì)越來越多。這個(gè)時(shí)候,我們可以將統(tǒng)計(jì)函數(shù)剝離出來,設(shè)計(jì)成獨(dú)立的類,以解決 Aggregator 類的無限膨脹問題。不過,暫時(shí)來說沒有必要這么做,畢竟將每個(gè)統(tǒng)計(jì)函數(shù)獨(dú)立成類,會(huì)增加類的個(gè)數(shù),也會(huì)影響到代碼的可讀性和可維護(hù)性。ConsoleReporter 和 EmailReporter 經(jīng)過重構(gòu)之后,代碼的重復(fù)問題變小了,但仍然沒有完全解決。盡管這兩個(gè)類不再調(diào)用 Aggregator 的靜態(tài)方法,但因?yàn)樯婕岸嗑€程和時(shí)間相關(guān)的計(jì)算

47、,代碼的測(cè)試性仍然不夠好。這兩個(gè)問題我們留在下一節(jié)課中解決,你也可以留言說說的你解決方案。重點(diǎn)回顧好了,今天的內(nèi)容到此就講完了。我們一塊來總結(jié)回顧一下,你需要掌握的重點(diǎn)內(nèi)容。面向?qū)ο笤O(shè)計(jì)中的最后一步是組裝類并提供執(zhí)行入口,也就是上帝類要做的事情。這個(gè)上帝類是沒辦法去掉的,但我們可以將上帝類做得很輕量級(jí),把核心邏輯都剝離出去,下沉形成獨(dú)立的類。上帝類只負(fù)責(zé)組裝類和串聯(lián)執(zhí)行流程。這樣做的好處是,代碼結(jié)構(gòu)更加清晰,底層核心邏輯更容易被復(fù)用。面向?qū)ο笤O(shè)計(jì)和實(shí)現(xiàn)要做的事情,就是把合適的代碼放到合適的類中。當(dāng)我們要實(shí)現(xiàn)某個(gè)功能的時(shí)候,不管如何設(shè)計(jì),所需要編寫的代碼量基本上是一樣的,唯一的區(qū)別就是如何將這些代碼劃分到不同的類中。不同的人有不同的劃分方法,對(duì)應(yīng)得到的代碼結(jié)構(gòu)(比如類與類之間交互等)也不盡相同。好的設(shè)計(jì)一定是結(jié)構(gòu)清晰、有條理、邏輯性強(qiáng),看起來一目了然,讀完之后常常有一種原來 如此的感覺。差的設(shè)計(jì)往往邏輯、代碼亂塞一通,沒有什么設(shè)計(jì)思路可言,看起來莫名其妙,讀完之后一頭霧水。課堂討論1. 今天我們提到,重構(gòu)之后的 ConsoleReporter 和 EmailReporter 仍然

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論