版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、下載APP39 | 運(yùn)用學(xué)過(guò)的設(shè)計(jì)原則和思想完善之前講的性能計(jì)數(shù)器項(xiàng)目(上)2020-01-31 王爭(zhēng)設(shè)計(jì)模式之美進(jìn)入課程講述:馮永吉時(shí)長(zhǎng) 12:29 大小 10.01M在第 25 節(jié)、第 26 節(jié)中,我們講了如何對(duì)一個(gè)性能計(jì)數(shù)器框架進(jìn)行分析、設(shè)計(jì)與實(shí)現(xiàn),并且實(shí)踐了之前學(xué)過(guò)的一些設(shè)計(jì)原則和設(shè)計(jì)思想。當(dāng)時(shí)我們提到,小步快跑、逐步迭代是一種非常實(shí)用的開(kāi)發(fā)模式。所以,針對(duì)這個(gè)框架的開(kāi)發(fā),我們分多個(gè)版本來(lái)逐步完善。在第 25、26 節(jié)課中,我們實(shí)現(xiàn)了框架的第一個(gè)版本,它只包含最基本的一些功能,在設(shè)計(jì)與實(shí)現(xiàn)上還有很多不足。所以,接下來(lái),我會(huì)針對(duì)這些不足,繼續(xù)迭 發(fā)兩個(gè)版本:版本2 和版本 3,分別對(duì)應(yīng)第
2、 39 節(jié)和第 40 節(jié)的內(nèi)容。在版本 2 中,我們會(huì)利用之前學(xué)過(guò)的重構(gòu)方法,對(duì)版本 1 的設(shè)計(jì)與實(shí)現(xiàn)進(jìn)行重構(gòu),解決版本 1 存在的設(shè)計(jì)問(wèn)題,讓它滿足之前學(xué)過(guò)的設(shè)計(jì)原則、思想、編程規(guī)范。在版本 3 中,我們?cè)賹?duì)版本 2 進(jìn)行迭代,并且完善框架的功能和非功能需求,讓其滿足第 25 節(jié)課中羅列的所有需求。話不多說(shuō),讓我們正式開(kāi)始版本 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è)類(lèi)。MetricsCollector:負(fù)責(zé)打點(diǎn)采集原始數(shù)據(jù),
3、包括記錄每次接口請(qǐng)求的響應(yīng)時(shí)間和請(qǐng)求時(shí) 間戳,并調(diào)用 MetricsStorage 提供的接口來(lái)存儲(chǔ)這些原始數(shù)據(jù)。MetricsStorage 和 RedisMetricsStorage:負(fù)責(zé)原始數(shù)據(jù)的存儲(chǔ)和讀取。Aggregator:是一個(gè)工具類(lèi),負(fù)責(zé)各種統(tǒng)計(jì)數(shù)據(jù)的計(jì)算,比如響應(yīng)時(shí)間的最大值、最小值、平均值、百分位值、接口訪問(wèn)次數(shù)、tps。ConsoleReporter 和 EmailReporter:相當(dāng)于一個(gè)上帝類(lèi)(God Class),定時(shí)根據(jù)給定的時(shí)間區(qū)間,從數(shù)據(jù)庫(kù)中取出數(shù)據(jù),借助 Aggregator 類(lèi)完成統(tǒng)計(jì)工作,并將統(tǒng)計(jì)結(jié)果輸出到相應(yīng)的終端,比如命令行、郵件。MetricCo
4、llector、MetricsStorage、RedisMetricsStorage 的設(shè)計(jì)與實(shí)現(xiàn)比較簡(jiǎn)單,不是版本 2 重構(gòu)的重點(diǎn)。今天,我們重點(diǎn)來(lái)看一下 Aggregator 和 ConsoleReporter、EmailReporter 這幾個(gè)類(lèi)。我們先來(lái)看一下 Aggregator 類(lèi)存在的問(wèn)題。Aggregator 類(lèi)里面只有一個(gè)靜態(tài)函數(shù),有 50 行左右的代碼量,負(fù)責(zé)各種統(tǒng)計(jì)數(shù)據(jù)的計(jì)算。當(dāng)要添加新的統(tǒng)計(jì)功能的時(shí)候,我們需要修改 aggregate() 函數(shù)代碼。一旦越來(lái)越多的統(tǒng)計(jì)功能添加進(jìn)來(lái)之后,這個(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è)賮?lái)看一下 ConsoleReporter 和 EmailReporter 這兩個(gè)類(lèi)存在的問(wèn)題。ConsoleReporter 和 EmailReporter 兩個(gè)類(lèi)中存在代碼重復(fù)問(wèn)題。在這兩個(gè)類(lèi)中,從數(shù)據(jù)庫(kù)中取數(shù)據(jù)、做統(tǒng)計(jì)的邏輯都是相同的,可以抽取出來(lái)復(fù)用,否則就違反了 DRY
11、 原則。整個(gè)類(lèi)負(fù)責(zé)的事情比較多,不相干的邏輯糅合在里面,職責(zé)不夠單一。特別是顯示部分的代碼可能會(huì)比較復(fù)雜(比如 Email 的顯示方式),最好能將這部分顯示邏輯剝離出來(lái),設(shè)計(jì)成一個(gè)獨(dú)立的類(lèi)。除此之外,因?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 的問(wèn)題進(jìn)行重構(gòu)Aggregator 類(lèi)和 ConsoleReporter、EmailReporter 類(lèi)主要負(fù)責(zé)統(tǒng)計(jì)顯示的工作。在第26 節(jié)中,我們提到,如果我們把統(tǒng)計(jì)顯示所要完成的功能邏輯細(xì)分一下,主要包含
21、下面 4點(diǎn):1. 根據(jù)給定的時(shí)間區(qū)間,從數(shù)據(jù)庫(kù)中拉取數(shù)據(jù);2. 根據(jù)原始數(shù)據(jù),計(jì)算得到統(tǒng)計(jì)數(shù)據(jù);3. 將統(tǒng)計(jì)數(shù)據(jù)顯示到終端(命令行或郵件);4. 定時(shí)觸發(fā)以上三個(gè)過(guò)程的執(zhí)行。之前的劃分方法是將所有的邏輯都放到 ConsoleReporter 和 EmailReporter 這兩個(gè)上帝類(lèi)中,而 Aggregator 只是一個(gè)包含靜態(tài)方法的工具類(lèi)。這樣的劃分方法存在前面提到的一些問(wèn)題,我們需要對(duì)其進(jìn)行重新劃分。面向?qū)ο笤O(shè)計(jì)中的最后一步是組裝類(lèi)并提供執(zhí)行入口,所以,組裝前三部分邏輯的上帝類(lèi)是必須要有的。我們可以將上帝類(lèi)做的很輕量級(jí),把核心邏輯都剝離出去,形成獨(dú)立的類(lèi),上帝類(lèi)只負(fù)責(zé)組裝類(lèi)和串聯(lián)執(zhí)行流程
22、。這樣做的好處是,代碼結(jié)構(gòu)更加清晰,底層核心邏輯更容易被復(fù)用。按照這個(gè)設(shè)計(jì)思路,具體的重構(gòu)工作包含以下 4 個(gè)方面。第 1 個(gè)邏輯:根據(jù)給定時(shí)間區(qū)間,從數(shù)據(jù)庫(kù)中拉取數(shù)據(jù)。這部分邏輯已經(jīng)被封裝在MetricsStorage 類(lèi)中了,所以這部分不需要處理。第 2 個(gè)邏輯:根據(jù)原始數(shù)據(jù),計(jì)算得到統(tǒng)計(jì)數(shù)據(jù)。我們可以將這部分邏輯移動(dòng)到Aggregator 類(lèi)中。這樣 Aggregator 類(lèi)就不僅僅是只包含統(tǒng)計(jì)方法的工具類(lèi)了。按照這個(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ù)顯示到終端。我們將這部分邏輯剝離出來(lái),設(shè)計(jì)成兩個(gè)類(lèi): ConsoleViewer 類(lèi)和 EmailViewer 類(lèi),分別負(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è)邏輯:組裝類(lèi)并定時(shí)觸發(fā)執(zhí)行統(tǒng)計(jì)顯示。在將核心邏輯剝離出來(lái)之后,這個(gè)類(lèi)的代碼變得更加簡(jiǎn)潔、清晰,只負(fù)責(zé)組裝各個(gè)類(lèi)(MetricsStor
31、age、Aggegrator、StatViewer)來(lái)完成整個(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)過(guò)上面的重構(gòu)之后,我們現(xiàn)在再來(lái)看一下,現(xiàn)在框架該如何來(lái)使用。我們需要在應(yīng)用啟動(dòng)的時(shí)候,創(chuàng)建好 ConsoleReporter 對(duì)象,并且調(diào)用它的startRepeatedReport() 函數(shù),來(lái)啟動(dòng)定時(shí)統(tǒng)計(jì)并輸出數(shù)據(jù)到終端。同理,我們還需要?jiǎng)?chuàng)建好 EmailReporter 對(duì)象,并且調(diào)用它
39、的 startDailyReport() 函數(shù),來(lái)啟動(dòng)每日統(tǒng)計(jì)并輸出數(shù)據(jù)到制定郵件地址。我們通過(guò) MetricsCollector 類(lèi)來(lái)收集接口的訪問(wèn)情況,這部分收集代碼會(huì)跟業(yè)務(wù)邏輯代碼耦合在一起,或者統(tǒng)一放到類(lèi)似 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 / 收集接口訪問(wèn)數(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è)類(lèi)各司其職。ConsoleReporter 和EmailReporter 負(fù)責(zé)組裝這三個(gè)類(lèi),將獲取原始數(shù)據(jù)、聚合統(tǒng)計(jì)、顯示統(tǒng)計(jì)結(jié)果到終端這三個(gè)階段的工作串聯(lián)起來(lái),定時(shí)觸發(fā)執(zhí)行。除此之外,MetricsStorage、Aggregator、Stat
44、Viewer 三個(gè)類(lèi)的設(shè)計(jì)也符合迪米特法則。它們只與跟自己有直接相關(guān)的數(shù)據(jù)進(jìn)行交互。MetricsStorage 輸出的是 RequestInfo 相關(guān)數(shù)據(jù)。Aggregator 類(lèi)輸入的是 RequestInfo 數(shù)據(jù),輸出的是 RequestStat 數(shù)據(jù)。StatViewer 輸入的是 RequestStat 數(shù)據(jù)。針對(duì)版本 1 和版本 2,我畫(huà)了一張它們的類(lèi)之間依賴關(guān)系的對(duì)比圖,如下所示。從圖中, 我們可以看出,重構(gòu)之后的代碼結(jié)構(gòu)更加清晰、有條理。這也印證了之前提到的:面向?qū)ο?設(shè)計(jì)和實(shí)現(xiàn)要做的事情,就是把合適的代碼放到合適的類(lèi)中。剛剛我們分析了代碼的整體結(jié)構(gòu)和依賴關(guān)系,我們現(xiàn)在再來(lái)具
45、體看每個(gè)類(lèi)的設(shè)計(jì)。Aggregator 類(lèi)從一個(gè)只包含一個(gè)靜態(tài)函數(shù)的工具類(lèi),變成了一個(gè)普通的聚合統(tǒng)計(jì)類(lèi)?,F(xiàn)在, 我們可以通過(guò)依賴注入的方式,將其組裝進(jìn) ConsoleReporter 和 EmailReporter 類(lèi)中, 這樣就更加容易編寫(xiě)單元測(cè)試。Aggregator 類(lèi)在重構(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)非常清晰,修改起來(lái)更加容易,可維護(hù)性提高。目前來(lái)看,Aggregator 的設(shè)計(jì)還算合理。但是,如果隨著更多的統(tǒng)計(jì)功能的加入, Aggregator 類(lèi)的代碼會(huì)越來(lái)越多。這個(gè)時(shí)候,我們可以將統(tǒng)計(jì)函數(shù)剝離出來(lái),設(shè)計(jì)成獨(dú)立的類(lèi),以解決 Aggregator 類(lèi)的無(wú)限膨脹問(wèn)題。不過(guò),暫時(shí)來(lái)說(shuō)沒(méi)有必要這么做,畢竟將每個(gè)統(tǒng)計(jì)函數(shù)獨(dú)立成類(lèi),會(huì)增加類(lèi)的個(gè)數(shù),也會(huì)影響到代碼的可讀性和可維護(hù)性。ConsoleReporter 和 EmailReporter 經(jīng)過(guò)重構(gòu)之后,代碼的重復(fù)問(wèn)題變小了,但仍然沒(méi)有完全解決。盡管這兩個(gè)類(lèi)不再調(diào)用 Aggregator 的靜態(tài)方法,但因?yàn)樯婕岸嗑€程和時(shí)間相關(guān)的計(jì)算
47、,代碼的測(cè)試性仍然不夠好。這兩個(gè)問(wèn)題我們留在下一節(jié)課中解決,你也可以留言說(shuō)說(shuō)的你解決方案。重點(diǎn)回顧好了,今天的內(nèi)容到此就講完了。我們一塊來(lái)總結(jié)回顧一下,你需要掌握的重點(diǎn)內(nèi)容。面向?qū)ο笤O(shè)計(jì)中的最后一步是組裝類(lèi)并提供執(zhí)行入口,也就是上帝類(lèi)要做的事情。這個(gè)上帝類(lèi)是沒(méi)辦法去掉的,但我們可以將上帝類(lèi)做得很輕量級(jí),把核心邏輯都剝離出去,下沉形成獨(dú)立的類(lèi)。上帝類(lèi)只負(fù)責(zé)組裝類(lèi)和串聯(lián)執(zhí)行流程。這樣做的好處是,代碼結(jié)構(gòu)更加清晰,底層核心邏輯更容易被復(fù)用。面向?qū)ο笤O(shè)計(jì)和實(shí)現(xiàn)要做的事情,就是把合適的代碼放到合適的類(lèi)中。當(dāng)我們要實(shí)現(xiàn)某個(gè)功能的時(shí)候,不管如何設(shè)計(jì),所需要編寫(xiě)的代碼量基本上是一樣的,唯一的區(qū)別就是如何將這些代碼劃分到不同的類(lèi)中。不同的人有不同的劃分方法,對(duì)應(yīng)得到的代碼結(jié)構(gòu)(比如類(lèi)與類(lèi)之間交互等)也不盡相同。好的設(shè)計(jì)一定是結(jié)構(gòu)清晰、有條理、邏輯性強(qiáng),看起來(lái)一目了然,讀完之后常常有一種原來(lái) 如此的感覺(jué)。差的設(shè)計(jì)往往邏輯、代碼亂塞一通,沒(méi)有什么設(shè)計(jì)思路可言,看起來(lái)莫名其妙,讀完之后一頭霧水。課堂討論1. 今天我們提到,重構(gòu)之后的 ConsoleReporter 和 EmailReporter 仍然
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 機(jī)關(guān)服務(wù)品質(zhì)承諾函4篇
- 企業(yè)運(yùn)營(yíng)規(guī)劃及年度發(fā)展戰(zhàn)略方案
- 童年的美好回憶:我與書(shū)的故事10篇
- 企業(yè)資產(chǎn)管理制度化手冊(cè)
- 供應(yīng)鏈管理流程優(yōu)化方案文檔
- 幼兒音樂(lè)游戲教學(xué)方案實(shí)錄
- 個(gè)人職業(yè)成長(zhǎng)保障承諾書(shū)5篇
- 質(zhì)量檢測(cè)規(guī)范操作承諾書(shū)3篇
- 高速公路養(yǎng)護(hù)作業(yè)實(shí)施方案范文
- 一年級(jí)語(yǔ)文重點(diǎn)課文教學(xué)設(shè)計(jì)方案
- 船廠技術(shù)狀態(tài)管理制度
- 旅行社供應(yīng)商管理制度
- 老年精神科護(hù)理
- CJ/T 461-2014水處理用高密度聚乙烯懸浮載體填料
- 重癥醫(yī)學(xué)科醫(yī)院感染控制原則專(zhuān)家共識(shí)(2024)解讀
- 數(shù)據(jù)治理實(shí)施方案
- 煤磨動(dòng)火作業(yè)施工方案
- 工程施工及安全管理制度
- 虛擬電廠解決方案
- 嗜酸性粒細(xì)胞與哮喘發(fā)病關(guān)系的研究進(jìn)展
- 《陸上風(fēng)電場(chǎng)工程可行性研究報(bào)告編制規(guī)程》(NB/T 31105-2016)
評(píng)論
0/150
提交評(píng)論