|
我是老溫,一名熱愛學習的嵌入式工程師$ n( t1 ]! l2 K+ E2 o# Y1 s8 e. n
關注我,一起變得更加優(yōu)秀!) r: L M3 x- w& l
一、原理篇( A+ M4 T2 s; {0 M
在軟件工程中,模塊的內聚和耦合是度量模塊化質量的標準之一。內聚是指模塊的功能強度的度量,即一個模塊內部各個元素彼此結合的緊密程度的度量。若一個模塊內各元素(語名之間、程序段之間)聯(lián)系的越緊密,則它的內聚性就越高。
" C- Z6 s% _( q" n2 \- q耦合是程序中各模塊之間相互聯(lián)系緊密程度的一種度量。各模塊之間聯(lián)系越緊密,其耦合性就越強。模塊間耦合高低取決于模塊間接口的復雜性、調用的方式及傳遞的信息。
0 z0 w2 S9 W6 W+ m3 w在程序設計中提倡的是高內聚低耦合。所謂高內聚,是指模塊是由相關性很強的代碼組成,只負責一項任務,也就是常說的單一責任原則,這樣的模塊,無論從設計、實現(xiàn)還是閱讀,都能體現(xiàn)出其保持專一性帶來的好處。9 x0 }2 `* S6 k7 \
而低耦合,是指模塊之間盡可能的使其獨立存在,模塊之間不產生聯(lián)系不可能,但模塊與模塊之間的接口應該盡量少而簡單。這樣,高內聚從整個程序中每一個模塊的內部特征角度,低耦合從程序中各個模塊之間的關聯(lián)關系角度,對我們的設計提出了要求。
1 s; c$ C [9 C程序設計和軟件工程發(fā)展過程中產生的很多技術、設計原則,都可以從內聚和耦合的角度進行解讀。作為C語言程序設計的初學者,結合當前對于函數(shù)的理解可達到的程度,我們探討一下如何做到高內聚低耦合。3 R f3 E; V9 w; d
針對低耦合。耦合程度最低的是非直接耦合,指兩個函數(shù)之間的聯(lián)系完全是通過共同的調用函數(shù)的控制和調用來實現(xiàn)的,耦合度最弱,函數(shù)的獨立性最強。2 D$ }4 K( q4 W1 {" b
但一組函數(shù)之間沒有數(shù)據(jù)傳遞顯然不現(xiàn)實,次之追求數(shù)據(jù)耦合,調用函數(shù)和被調用函數(shù)之間只傳遞簡單的數(shù)據(jù)參數(shù),例如采用值傳遞方式的函數(shù)。* c8 g% e+ e& c6 P- q6 m+ b
有些函數(shù)數(shù)在調用時,利用形式參數(shù)傳地址的方式,在函數(shù)體內通過指針可以修改其指向的作用域以外的存儲單元,這構成了更強的耦合,稱為特征耦合,在這里,使函數(shù)之間產生聯(lián)系的是地址這樣的特征標識。另外,有兩個函數(shù)可能會打開同一個文件進行操作,這也構成了特征耦合的一種形式。. j+ y" k: m0 K9 A& e/ m
更強的耦合是外部耦合,這里,一組模塊都訪問同一全局變量,而且不通過參數(shù)表傳遞該全局變量的信息,當發(fā)現(xiàn)程序執(zhí)行結果異常時,很難定位到是在哪個函數(shù)中出了差錯。不少初學者覺得參數(shù)傳遞麻煩,將要處理的數(shù)據(jù)盡可能地定義為全局變量,這樣,函數(shù)之間的接口簡單了,但形成的是耦合性很強的結構。
& o+ T2 K" i) z7 t在C語言中,還可以通過靜態(tài)局部變量,在同一個程序的兩次調用之間共享數(shù)據(jù),這也可以視為是一種外部耦合,只不過靜態(tài)局部變量的作用域限于函數(shù)內部,其影響也只在函數(shù)內部,耦合程度比使全局變量也還是弱很多。由此,我們可以理解前述在使用全局變量、靜態(tài)局部變量時提出的“用在合適的時候,不濫用”的原則。
) T: V6 W7 Y6 r$ ?2 J# B針對高內聚。內聚程度最高的是功能內聚,模塊內所有元素的各個組成部分全部都為完成同一個功能而存在,共同完成一個單一的功能,模塊已不可再分。這樣的函數(shù)功能非常清晰、明確,一般出現(xiàn)在程序結構圖的較低被調用的層次上。' ?; q" @& I' E' ^ @% P
次之的是順序內聚,一個函數(shù)中各個處理元素和同一個功能密切相關,通常前一個處理元素的輸出是后一個處理元素的輸入。對于這樣的函數(shù),如果不致于產生高耦合的話,可以分開兩個函數(shù)實現(xiàn)。
- t: t; k8 E9 Z有的函數(shù),其中的不同處理功能僅僅是由于都訪問某一個公用數(shù)據(jù)而發(fā)生關聯(lián),這稱為通信內聚和信息內聚,內聚程度進一步下降。內聚程度再低的情況就不再一一列舉,最差的偶然內聚中,一個函數(shù)內的各處理元素之間沒有任何聯(lián)系,只是偶然地被湊到一起。8 ?* E1 Z1 G; Z+ f0 B2 L6 ~3 R
可以想像這樣的模塊東一榔頭西一錘子,類似一個毫無凝聚力的團伙,對應的是低質量?傊,在解決問題劃分函數(shù)時,要遵循“一個函數(shù),一個功能”的原則,盡可能使模塊達到功能內聚。+ N+ [5 o6 g7 d: R1 P* V6 l
要做到高內聚低耦合,重點是要在寫代碼之前花些時間做好設計。在下面的例子中,將討論結合具體的問題,如何將以上的因素考慮進去。
' k5 L0 i4 ~5 o( K6 H$ o* E二、示例篇本例受裘宗燕老師《從問題到程序——程序設計與C語言引論啟發(fā)》。 \# E9 z( C% n
任務: N. d( c# w! k& h0 E% C' F
輸出200以內的完全平方數(shù)(一個數(shù)如果是另一個整數(shù)的完全平方,那么我們就稱這個數(shù)為完全平方數(shù),也叫做平方數(shù)),要求每隔5個數(shù)據(jù)要輸出一個換行。
* k' X. T4 m$ z解決方案及點評4 i% }* m6 S, r+ ^7 @# p# B
對于這個簡單任務,我們在一個main函數(shù)中完成了任務。程序如方案1:: f- p& p1 i8 T
//方案1:內聚性較高的單模塊實現(xiàn)方案
. m0 [; R; a5 N& P+ T* l9 Q#include
1 P% k' M% ~( }3 O5 Kint main() Q/ A8 d$ c7 M/ W
{8 l9 ]4 X0 G, ~8 V; C
int m, num=0;
`; o2 J% e9 a) x for (m = 1; m * m 200; m++)% K3 R1 h1 V2 g0 m) [( h2 p: ^
{
2 j# L0 q# n i2 F( _ printf("%d ", m * m);5 J8 e: }( A& @8 u I7 d' L# F
num++;) Z! r% d1 ~, f2 ~ G
if (num%5==0)
5 H+ [3 x$ e8 S4 x$ X8 z* D printf("
8 ^/ |: x7 ~7 \");
$ c4 S4 t2 k0 O& o# M' L }
& J3 r; `" i" G/ f8 r4 A return 0;
. m, X- i+ B% c+ ] t}5 \% A) i, r2 R, i! @
由于任務本身簡單,將之在一個main函數(shù)中實現(xiàn)后,這個函數(shù)的內聚程度接近功能內聚,已經相當高了,就任務本身,不需再進行分解。為使讀者能深入理解模塊質量方面的技術,我們將試圖將內聚程序再提高一些,然后考察耦合程度不同的各種解決方案。' N6 g: q6 r& j) k: _
要提高上面解決方案中函數(shù)(僅main一個函數(shù))的內聚程度,我們考察程度的功能“找出完全平方數(shù)并輸出”——“找出完全平方數(shù)”和“輸出”這本身就是兩個功能,再細分輸出時還有“要求5個數(shù)據(jù)在一行”的要求,這些功能的實現(xiàn)細節(jié)都在一個函數(shù)當中,可見是有余地再提高內聚程度的。
. e0 B& [. z }3 [( A在實現(xiàn)的應用中,幾乎所有的處理都可以分解為“輸入-計算-輸出”的模式,優(yōu)秀的解決方案往往至少要將這三個模塊都獨立出來,對于“計算”模塊而言,其內部不再包括輸入輸出,專門接受輸入的數(shù)據(jù),計算完成后返回結果即可。當然,對于復雜的問題,在各個環(huán)節(jié)上可能還需要再做分解。6 j9 b4 a6 Z7 j% N) `. Y
下面,我們探討將“找出完全平方數(shù)輸出”和“每5個數(shù)據(jù)后換行”分開實現(xiàn)的方案。這樣的分解有助于提高內聚性,與此同時,分解后的兩個模塊間的耦合程度,成為我們要關注的焦點。* W* v& Q2 w; p9 X6 u6 S5 r
現(xiàn)在將“找出完全平方數(shù)并輸出”的功能仍放在main函數(shù)中(獨立成為單獨的函數(shù)也可以,但不必要了),而“每5個數(shù)據(jù)后換行”的功能,設計一個名稱為format的函數(shù),它每調用一次就輸出一個空格作為兩個完全平方數(shù)間的分隔,而每調用到第5次時,輸出的是一個換行。' f7 p s3 Z7 s9 I4 A' d
這兩個模塊之間,需要有一個“現(xiàn)在是第幾次調用”的信息需要傳遞,不可能用耦合程度最松散的非直接耦合.我們考慮數(shù)據(jù)耦合,用簡單形式參數(shù)傳值,得到方案2。
+ o: b- o' W( Y. d$ A8 R//方案2:一個耦合度低,但不能完成功能要求的解決方案
% |2 J" a5 T0 [9 R8 W! q9 Q* n#include 5 y/ P# w& n0 I1 ^
void format(int);! v: @' S0 K3 g5 a" g/ B) Z% O; I
int main()
' ]3 R7 Y/ n/ z2 V* A0 Z{' n- e6 t: ~* \0 I# N7 O: P" F
int m, num=0;
6 o" d5 d8 }2 T$ S& V* Z6 L for (m = 1; m * m 200; m++)
! y5 j" @' ?6 q+ m4 Z* j0 L {1 C( U, [2 Z/ r
printf("%d", m * m);& `" {: Q- z9 q: E$ `; f5 t
format(num);
/ S& t( X3 L) K$ f/ y* C }
: k* e7 {0 Z+ e1 |6 I) P( M# p return 0;
! w7 s7 q2 m1 c" Y8 K}
: q/ `$ \( z+ Mvoid format(int n)
- m0 b% y% r3 J1 F1 s) z$ A{4 A5 A0 N8 l3 w- p6 ^& Y$ @
n++;
" k! |6 n8 [& v if (n%5==0)2 b6 S- b- d# L+ y
printf("
# Q+ `8 A- s' p( ?% m) p");
# R7 D7 B. F, }# ? else) }7 [/ V" w' O. S* O4 O
printf(" ");, A s0 X4 k) P) c. b3 M
return;2 g9 s8 T, c( ~* r
}2 U9 x- D3 y) L9 k ^
在這個程序結構中,format與main函數(shù)的耦合程度為數(shù)據(jù)耦合。在main中定義了局部變量num,在一次都未輸出時,置初值為0是合理的。在調用format時,將num傳遞來的表示第幾次輸出(第幾個完全平方數(shù))的形式參數(shù)n,n自增1,然后再控制輸出空格或換行。
* R8 z g% x5 p& t( x1 P4 d然而分析和運行程序發(fā)現(xiàn),“每隔5個數(shù)據(jù)輸出一個換行”的功能并未實現(xiàn)。因為形式參數(shù)n在函數(shù)format內的改變對應的實在參數(shù)num占不同的內存空間,n++修改的結果,對num無任何的影響,導致了在下一次調用時,丟失了“輸出的是第幾個”的重要信息。一個補救的方法,是由format將變化后的n值作為返回值,再傳回給main函數(shù),得到如下方案3的程序:' f) m9 `8 M: V' A, q$ u& P. n
//方案3:利用了返回值使耦合度增大,但功能得以實現(xiàn)的方案 B1 [' N9 p: ~1 e, `0 B
#include
( F8 i$ j8 b% A7 C8 W: I9 |) E. V) [int format(int);9 m% S' b3 s, c7 e
int main()
5 @/ B+ P$ o0 w/ d: M. X- i8 f{
% w0 {; f D1 O T int m, num=0;
: w4 \( O& u' }8 X g3 v for (m = 1; m * m 200; m++)) H$ N6 f, l. {, R
{8 g, t5 N5 q$ W1 v" W' X) ^
printf("%d", m * m);. \" r, \2 C4 F4 L- Y5 ?# C
num = format(num);1 y' A6 P0 R, ]3 [3 v
}, n2 D, V+ {+ c6 z# ~
return 0;
) q2 B* ` m' `}
% D7 r3 M. z( G0 X: m# ~8 S, Sint format(int n)
^9 x6 k% A' q% d/ o7 [1 |' G{
5 s$ t! |2 C0 u7 W n++;! @! R( O+ w/ ?$ M P; {! c+ B
if (n%5==0)' B) G! B' p1 @+ E
printf("
. I- b' x1 {. b$ I. e- T");* L% w, j1 |0 o. {' J" D
else
' M* H' S4 _+ H) n/ _ printf(" ");
5 N9 t$ ]9 }& L$ T7 W3 L8 P return n;7 M( K" ^) f# Y) `, L6 c7 W
}
Y9 l$ j: V- f" M6 |! k, `: V) c維持原函數(shù)返回值為void,而將參數(shù)改為傳地址,得到下面的方案4。這個方案的耦合度更高一些,但功能還是能夠實現(xiàn)的。
/ P. k1 M5 `& Z/ s$ C; j//方案4:傳地址實現(xiàn)功能的方案,耦合度更大6 i- A5 [* @1 i: O& m4 Q
#include ; z7 J& y/ H' {/ ^1 b9 o( o
void format(int*);
( ]) Q" [7 I* Q j1 e& ]int main()0 {* d4 \+ ]% |2 P4 g0 m( Z* x/ p
{3 M- p# U8 m$ y5 k$ T8 ^
int m, num=0;
5 t- k9 V/ m1 S1 b. k for (m = 1; m * m 200; m++)
9 _: E' P1 `0 g4 m {
+ g7 d }. h# }9 r1 z" e3 y printf("%d", m * m);' {4 G2 I& b' Y3 [
format(&num);* E Y& s& s6 j! s; k
}/ q7 P1 y. Y9 g: ]; l5 a3 ]
return 0;2 J4 N- ~+ q+ P" ^# H' S
}. Y! K2 p2 o5 k7 L) r
void format(int *p)" ~# t* k4 w5 q& x; O: V( ]
{. D% }1 D+ A% ~& N9 p9 ^8 W
(*p)++;
/ k- e2 S3 V$ E+ T, y if ((*p)%5==0)
$ L' j- t& Y, y; O2 o printf("
' d& | e& v0 F! a1 \"); H% ^( A) `2 p6 M0 N. G, A
else% Z8 F) S( X) }5 k5 j7 s
printf(" "); z1 k; @; i8 r" }. [! E. _
return;! o1 r) p7 ]4 n
}
( S" R, C! n# j W一定有人想到了用全局變量的解決方案。這樣,可以將num定義為全局變量,num的生存周期不再依賴于函數(shù)調用,其值也能在函數(shù)的調用之間保持不變(只要其間沒有另外給它賦值),從而可以完成傳遞信息的任務。這時,format因為無需參數(shù)傳遞,可以設計為無參函數(shù),得到如下方案5的程序:
: j% o$ M( R& i7 c) v: h//方案5:耦合度最高的全局變量方案
: A& Z2 d: ^, v+ y& W/ H, s+ g#include
7 v8 q2 b' n4 H9 T: n4 b5 l% Vvoid format();
" t* `, T3 }5 H) q' }5 m3 iint num=0;* F" b8 K. U& Z" C4 U, ~. N
int main()
# I& v. z+ A# d* Y4 j9 o" n{
9 \1 V2 a. e7 m7 ^: j int m ;1 a, Q5 L" f, V+ y+ g) F F
for (m = 1; m * m 200; m++)$ I# b8 v9 ~, E, `" O& i9 D( s* D
{5 p% R( o" A0 M4 g! b0 I
printf("%d", m * m);
- U9 Z2 E* M. q/ C format();
; B/ U5 w& r. X% t+ F \# k1 B A }1 _" U( }9 J+ V. l: `$ U3 j
return 0;
6 d3 {, c) G( B2 Q( D}
6 i+ P3 i h- H/ Q3 y; f( e, c. w tvoid format()0 X# [) Z/ R8 Q9 ~. [
{
4 D8 Z- Y6 m/ ^ num++;# r- f5 f6 l; I0 f2 [
if (num%5==0)' J8 e. X9 l. }+ U& Y4 H1 c5 w+ ^
printf("
! l1 `- F& A- X# N* E1 d");: z, P% w" G9 z$ r S
else
1 }2 P% g& _) n/ F' ? printf(" ");- g' Z$ v( s& C7 X" v& T
return;
, {) K: H5 f' \6 h& J0 r}
; `1 L, j. a- R; y+ J& @這是解決這個問題的耦合程度最高的一個方案。將num定義為外部變量,意味著如果還有其他函數(shù),num是可以被任何函數(shù)修改的,當發(fā) format 計數(shù)錯誤時,尋找錯誤困難,而修改后又可能會帶來其他地方的錯誤。
5 U# a7 D i: m# x/ Q3 h在這么一個短小的程序中,這種方案可能尚可接受,當程度的規(guī)模稍變大,可能帶來的問題必須高度重視。因此,在實際應用中,強調全局變量要慎用(不是不用)。
; _( ?* h: d7 R$ y考慮到num是在format中應用的私用數(shù)據(jù)——只有format才關心這到底是第幾個數(shù)據(jù),main本來都不用關心的。這樣,可以考慮將num定義為format中的局部靜態(tài)變量,得到方案6的程序:
8 P3 O1 j) g( A( w, X. B5 V//方案6:用靜態(tài)局部變量,耦合度偏高但封裝性最好的方案# s1 Q5 e6 b3 l4 `
#include ( q0 t+ z+ v. J
void format();2 c+ S& x& ]$ E) x! Q
int main()
" s' i' |5 W0 Z% A, W) L. V{' R0 _0 R5 O% D @4 F- N
int m ;
/ j" v6 T/ S" n( X2 G for (m = 1; m * m 200; m++)7 Q/ y- }3 o0 x/ T- M$ }+ a- q
{% ]* p6 f' N4 Z1 t0 G
printf("%d", m * m);
4 w! f9 W% v( g/ j format();
& a b5 R" s* n9 ? }
0 x7 m! n5 n( w! o% Z return 0;5 B% J K# M/ a! U# j% w1 L6 ~, e
}- @# i) Q* w4 I
void format()- ^# \ `7 O: l! z( C3 n
{
; ?3 Y! l n" ^6 F7 ]5 p+ h static int num=0;
* ? x) k, L: A8 f& ?* }: S num++;) U, f( Z6 u2 w
if (num%5==0)% _ k+ s' G, q2 V* P0 s- F
printf("4 t# Y$ X' x) ?% ]/ X
"); {: V; o4 G8 \
else
. x9 [6 B$ l3 G/ G printf(" ");% p, F- y: J. Y% O! C; h4 S
return;
5 B6 @ A) l+ P8 @}
8 ^: l+ X4 p" K0 S' ?5 k- l在這里,靜態(tài)局部變量num的作用域是局部的,定義在函數(shù)體里,封裝性在所有方案里是最好的,從而能保證信息的隱蔽性,避免其他函數(shù)無意的越權訪問;
( x8 E) a( f: f9 B不過,num的生存期是全局的,可以跨越函數(shù)的不同次調用,在兩次調用間傳遞信息,耦合程度(自己和自己的耦合)要高一些,但使main函數(shù)和format函數(shù)的耦合達到了最理想的程度,既保證了功能的正確,又保證了局部數(shù)據(jù)的安全性,表現(xiàn)出靜態(tài)局部變量的優(yōu)勢。綜上所述,在解決一個問題時,存在著諸多的方案。
# X- x' y9 g$ T方案1可以接受,但希望提高內聚性而做出改進;方案2用簡單的參數(shù)傳值方式實現(xiàn)耦合程度低,但很可惜不能完成功能;在其他方案中,對于這個問題,選擇的優(yōu)先順序是:
) _9 Z _# n, v$ a7 M3 Q: I& V( q方案6、方案3 > 方案4 > 方案5
9 S8 V; b# I" z7 p: B, j( s建議讀者回顧前面的內容,想一想這樣排序的理由。在上述探討各個方案的過程中,我們應該體會到在程序設計能力提高的過程中,不斷地學習新的技術,懂得新的評判標準,這也就是一個不斷拓寬眼蜀的過程。
5 l6 f( G5 F1 |5 g9 p$ l在稍后的練習中,不妨多想一些方案,也能夠從專業(yè)的角度評判方案的優(yōu)劣,最終做到的,就是出手就是最佳方案的專業(yè)水平。
8 Q; ~2 c6 y# ]5 C$ \- E- I/ |
3 N: N8 r$ B+ o4 ]原文鏈接:https://blog.csdn.net/sxhelijian/article/details/79401703& W/ b$ a8 `- w, H$ y* O
. M4 @, m; @9 Z$ T. `0 \2 O-END-
* e( T9 u G& \往期推薦:點擊圖片即可跳轉閱讀
% D% ~* z1 n& Z' K! E# s 6 o) x% l) q# {, Y/ L/ w
/ K/ v8 k3 A$ w% @4 S0 q
6 |% ?, F2 y, l0 [% K. n( x
8 o* o, P/ F% [! L
qoxd3vs1blj64028584325.jpg (102.98 KB, 下載次數(shù): 4)
下載附件
保存到相冊
qoxd3vs1blj64028584325.jpg
2024-9-30 23:13 上傳
$ H* _9 q& E) M+ n( w
+ k* G8 a" S5 v) x' a, a, v L+ _
今天不秀 C 語言代碼了,秀一下注釋!
% h1 s' H. k% E0 V9 j0 v+ r
3 ?% G( {5 A. M2 q" x+ c + X: f- ?- s J1 O( J, \7 S# ? n. b
( R8 J; |7 y) d6 @6 }: Y- m0 D4 Y
" \( ]% a$ y5 z
, c( D s, m2 v$ q7 m$ @
4 X. C$ t/ g9 z0 H
5 n [$ e3 M9 D! N0 u9 |
! V# o+ o( e: u& \0 s
: Q8 F0 E# V$ E! H4 M& w9 Q W+ L 2 v! i* l3 c) q) |* F( o
u3qworrhkj564028584425.jpg (110.18 KB, 下載次數(shù): 6)
下載附件
保存到相冊
u3qworrhkj564028584425.jpg
2024-9-30 23:13 上傳
% a1 m2 s( d! y- l% ]# Y
. Y2 A5 ~. [' {1 y8 {# k/ B 真香!在嵌入式開發(fā)板上面適配 OpenHarmony!
0 z: L! n/ j8 D5 |
1 I! z+ g; z: h. o2 G
% \2 P1 E' q8 N " v; S1 i2 I! Y
Z! B+ x4 j) A: S; H+ s, f* B/ u9 M. \
# e. c0 v7 j: m4 W! ?4 u
! C7 L* G0 Q% x , D& {( G' n: }2 s5 u
# e" S! d5 J4 s- o# K
/ t- g: z% W! T& c0 H: n 4 X9 B9 _- ^+ x( e1 J
4slllxjrys064028584525.jpg (195.6 KB, 下載次數(shù): 3)
下載附件
保存到相冊
4slllxjrys064028584525.jpg
2024-9-30 23:13 上傳
$ J$ E* C0 N8 K+ q/ a9 l$ }! E
: i' F5 g7 q" Y# q; R- G 用模塊化和面向對象的方式,編寫單片機LCD驅動程序1 N2 U E: H: b, t( x6 b
9 W3 t" H1 {* z& r! A4 _
4 ^4 H) ~. N: z! y6 E
# S7 T) a% x1 m U2 A
& Y7 y: C( n6 N& z
" n$ p/ Q: m* V4 k# F+ N我是老溫,一名熱愛學習的嵌入式工程師
; E- I% t- T! Z- {關注我,一起變得更加優(yōu)秀! |
|