From 3d4b07c03764e546c59146fce5de5080f299f9f4 Mon Sep 17 00:00:00 2001 From: Hayden Hargreaves Date: Sun, 9 Nov 2025 22:46:43 -0700 Subject: [PATCH] (ARTICLE): Finished the `D`, now just onto final touches and DRY. I did push a lock file, I am tired of having to ignore it... and gitignore is not working for some reason. --- bun.lockb | Bin 113159 -> 115463 bytes .../SOLID Principles: Writing Clean Code.md | 122 +++++++++++++++++- 2 files changed, 120 insertions(+), 2 deletions(-) diff --git a/bun.lockb b/bun.lockb index d2ac3d49dad5990e7531b20d5bfabe17b4059aa3..4c0b03e92b22ed2b0f5b9d18078bbbac4012f160 100755 GIT binary patch delta 4424 zcmZ9PTWnlM8OP7qPU6d2UEAy6)R)F_ypAuiH@=SVyIEh8IPv{b3bm0UTGS?PnwC?k zkSr}pTR>`=hkc34ko zbN%L<@0&Tk{n_E$w~oBpB}!lGJ}K_Kwj3{MY=6&}wd!VVxq7!YcF4moqC{*&1+@`U zl}b#FC|MU3o=VlFt4)h4^T4lCJyqMG4mK*(-MT7jU7|!BeW|sGrKzln%KR9QhZY!5 z$IN&++oD?KMlnZy#LQ9O2W!gIeoKXVuAWB~G=i?@5lUeP=CJC7%8a4!QLbax?{q`7@ zt7Kd%-5x17jRa81!5*9q>`V83&Ec6Hf5>qv?^;1{i(YnYNjsKL6J^0!=|1pHDhhs z@8MIFSEzf;nU?F}GmX61gRyK|qvh#v?ETZcPFe@bi}y4*&9+-Rd&$n;tL&@ZuwT%d zZ)f64_OXYT`$7-vKHd-7>7eg(<|IP*`-#vrGATh0OQgd9I>VFU%4o!aaRw$AElt~6 z4${C;vY&gFa)|evku&}Ipr+uUGBLnqQJ!Idd)2AbuvB*k_`tvzV^ZC>3PBHBW6K>;%dA< z%vQo?vmqlk85*VXM+)9Ujkc;x<&c5*N4QrRho$6}4eTvPX-5Ucs^-4~o;T-tHz}Es z%eMb0t!LDvYL1>_EWb8$wfbXJUnryXQkfX%b~drcsol5@LUTlnvz_)>nkWi1e}bz@ z=%&$D3A*`%2{VwGOcyp~e=>g>)!{Unq$HXwN}|cA`s?qaDmukS&=K~_RNhM&?9Sb( z{B=V17wU|X(?!X30f)%+11A2a!^jS%d8`=kTC#X0$q0P~wU`qUzZ&`qqOeLv)JLQF zvnk9;nKJ83rPz7cV4TA`mG_4Ui@+*&tUUX<06fcW?U{0x=cg>h{MCi|1N6(EQ;Bqp ztSluPJ?|K|n#E$%RYfO#IzP1ej0&vci$l(ET{(Mq#?2XRYdJ!L8QQ{G_8=WC{w!Z2 zhhYIy8TrcImf%#OKtUBk^h4^duz;xXPQ4BQ8(u~ z-4pfr9-TKEnww{jm+&So@PvnXISc5qz&&X17Q%!M7HpE5J907Hi$pgJTV%s3X-vJy zW7;Jxk`vCE#7faKL(d#N#d#{HXNI0RdWs8FPR|TIbMzETR8G&#Qa*MlT9$a$T3U&` z$ci`3zsO!MDR|vnind8Jd)`s<5_ji1-%;98furQI)6Y@bQGui6iqmh!xGb|`mYSod zSmmYKn_-peZJ9kTR(XUP-qtnKBDH2l$j~#lc8RXV=(=+{tcM%TjCsFIsuRQa2LoW z2E&~6x57?(i`UBMP_R`LnA`Sh-kEQ*w#mP*b6p1cF2gGqBDDkXE66B zov{zq-&9U#I z?QQT1co{qnz6Ku4Hn)fuT9@#OZSu~6Ij{g0!38h_&S!HiV!Y-nn062Bf;MnGTm6ve zyD@^-esBg%gF%o25{!Z&Fbu}QB$xnG;4DZ3+=bp4NP=F>_9O5H_%Za?@z)1WgX`c) z@C3L9u7Z8x-SDp9Ote9sD4>*ecRB|HkXTz-Qn;;D6cITSZ%Z0~T+AHSlK? z{{#FD{1bTHD8#R+-^AUCJLM-t(~-*MelESWd#ATk?DfWtGw^FLj=`^T;_1szK709E z@2r3Q^0g~_Ppngoq>kF=SMTM!JtL=%pUEaWMZ=x>ZSmpW F{{d`vu!H~r delta 3988 zcmY+HTTENY8OP6%II;0ba{?x75+DWwnEL@taKMXANWffU5(p*)V$-hVRM}lN*>0)S zG;FusknKf;QDrK1C0bP~+cp)wDdb@#w@k27 zdH!>s@B8MP!=v|)Kl=RGY6B~H(s-Ude-f!Js4n~UlD}xJ*v?Z$x=q(K?KqE@v~qi~ zZYzX3DpjP|;ECdZ&54>qS+j@s0w^Yx*JbeCB9kw=blHb$x1mHIImcRw!QCYRc7o59 z1euG!Rn*ASB`zDwPvSYP+m1jTky9q1s-WyrZCO;tqC&5zJy$F$ZBcHw&Z>B`JE+XO z=~hP{Sd_n1ZCkde9g8x~s6C^3Ras^lFzTG9Eg!{U4iB_{Mr`Hf8Fed5XVpnk7G*Cp zc&5hWkuu$8%NuDO>d*^&P>1I9mXkTjfQwLWj4P1l+*Z|Eh1wL(tG-s#DAKf1>>UHO z!_m7HI&slQN8)s)tK2+zFKBjxQT9+Ng({*%}$iE&hMS_#AlGot3 z{^=DnZ=ty34X|S{pU-|y)9%5$j8D`L;(nVvU9U!vyMYv~6@wDjlUCSlF!)Nj$qyR* z+-wYSqte0OZR`?j>@@ntkI|&_#iAyj8gTHLCZ{Ni>WwBp-)nO743;yS)f>MAB|`hC zIHZ~BwXS-F|`kJY; zFXURn5czJa5HwZV4Dsbx9D1#$wPO>)O#4}xOMyIBAXHP9oPRlI?ae&h%PX*P}*+b$%h5bXS zQPxo_ys{Vab5Ea3@$F)t8avxiBJIo6zII7RjLemE@bD!(v2`Y2MNQu9yO*dh?Z(ru z!*toJljt}2(iM|$^;5ngi)5eD zOJU`<^AwbD{t0;t>_f^8iwsf6QvTV9$$vOR_nKzk%eK+$=^dt($wG@zc1%fXd-%}W zYa_H689O{ePF_W=*eT7tJE9|dy%-6~WJhx(MrkKBZfjJ>r4=7Yi#HwoU!zW2OMbP` zSZ=O1c<%AoNtuPyV}5RoyI5n+wXt!yc07PE_;@^MJC+~rzpAt9T&Lx$@`~-C4oT)} zFqfjE5&HHk;D7g-Mp)CvvsYbYXWH<7gd)|PFnD+(B%Pl^c{f{*T*J|?QT-munQVFS zI-ZT|vVLt6(K<<8R#D!}mOWEyqDoBZtcX9D3M$_@8T1h0Pp@0(hHfj*Z*0Y)(iY{O zR(r-QYSW?)EXp6%Svenz29_Z?X$Y=6iz_#5t+s4SsJy6a%MKn!?%3$iy|9ww{o$*HkZ2tBF_!YIS=2()j1Jn zB8WC_JNRGcoYG{vyak?lKab73Y^Sj(ea>&q%TUP7(|3I6Ha!cp-pXxy>(i(`jAM69 z4vEFoOTGyux=;c*h#A}-56KW+j^{Sbcj8Ei=h}=Iy%KUtjG@J%3lv!!3v&AX1>COR zzFesI-R1E9K!2ZHxf zS|7Hdhz|v=1NZ=50__6m0)BwEQalY>4?wQdUIqBN(SjfZ`oJa74_dI=Pry&X&!E4K z-v{6u;2{uM@?G#v@E-UU!_?nK;eGH`umtXc*TEa$O|S^w0{6gu@HY4gcqjS7%g&bn z2J8F|{2n|7zXF@!Bk=QNO+BlAxPszi@B#P^_#U_n20#om#la0Q0j_~MP!CLC2Zg{1 zK0*IKfi(CF7z9W$+7K89BLM$$Xk!3*N4p9l!g>=Z;E~X-gGn$2Zh&bJ1vB6#m<6{0 z{^!!>!R;h#U>zrZiQ+H7kHCk?z6RER>fb2-2Ye3x2mYJ > "Let q(x) be a property provable about objects of x of type T. Then q(y) should be provable for @@ -463,7 +463,7 @@ expectation for a rectangle is that the height and width will be set independent ## Interface Segregation Principle -The interface segregation principle states: +The [Interface Segregation Principle](https://en.wikipedia.org/wiki/Interface_segregation_principle) states: > > "A client should never be forced to implement an interface that it doesn’t use, or clients @@ -569,6 +569,124 @@ public: ## Dependency Inversion Principle +The [Dependency Inversion Principle](https://en.wikipedia.org/wiki/Dependency_inversion_principle) states: + +> +> "Entities must depend on abstractions, not on concretions. It states that the high-level module +> must not depend on the low-level module, but they should depend on abstractions." +> + +This means that a class should rely on abstractions (interfaces or abstract base classes) rather than +concrete implementations. The Dependency Inversion Principle (DIP) is a specific methodology for +creating loosely coupled modules. When implemented, the DIP allows high-level modules to exist +independently of the low-level modules. + +A nice analogy I found states: + +> +> *"In a software development team, developers depend on an abstract version control system (e.g., Git) +> to manage and track changes to the codebase. They don't depend on specific details of how Git works +> internally."* ~geeksforgeeks +> + +### Code Example + +To illustrate the DIP, we will use an example in which a high-level `Notifier` needs to send messages +to a low-level `SMSService`. + +The initial implementation will work ***fine***; however, it violates the DIP. What will happen if the +requirements change and the `Notifier` needs to send messages to a new service, such as an `EmailService`? + +```cpp +// Low-level Module (The Detail) +class SMSService { +public: + void sendSMS(const std::string &recipient, const std::string &message) const { + std::cout << "SMSService: Sending SMS to " << recipient << ": '" << message + << "'\n"; + } + // No abstraction/interface here +}; + +// High-level Module (The Logic) +class Notifier { +public: + Notifier(const std::string &recipient) : recipient(recipient) {} + + // DIP VIOLATION: Notifier (high-level) directly depends on SMSService + // (low-level). + void send(const std::string &message) const { + SMSService smsService; // Direct, hard dependency on the concrete class + smsService.sendSMS(recipient, message); + } + +private: + std::string recipient; +}; +``` + +By updating the code to adhere to the DIP, we can allow our `Notifier` class to send messages to any +client through the use of an abstraction. + +```cpp +// 1. Abstraction (The Contract/Interface) +class IMessageSender { +public: + virtual void sendMessage(const std::string &recipient, + const std::string &message) const = 0; + virtual ~IMessageSender() = default; +}; +``` + +Then, we can create our clients; for this example, we only need two of them to demonstrate the +power of the **Dependency Inversion Principle**. + +```cpp +// 2. Low-level Module depends on Abstraction (The Detail) +class SMSServiceDIP : public IMessageSender { +public: + void sendMessage(const std::string &recipient, + const std::string &message) const override { + std::cout << "SMSServiceDIP: Sending SMS to " << recipient << ": '" + << message << "'\n"; + } +}; + +// We can easily add a new service without touching the Notifier! +class EmailServiceDIP : public IMessageSender { +public: + void sendMessage(const std::string &recipient, + const std::string &message) const override { + std::cout << "EmailServiceDIP: Sending Email to " << recipient << ": '" + << message << "'\n"; + } +}; +``` + +Finally, we can reimplement the `Notifier` class to depend only on the abstracted class and adhere +to the DIP. + +```cpp +// 3. High-level Module depends on Abstraction (The Logic) +class Notifier { +public: + // **Dependency Injection:** The concrete service is passed in via the + // constructor. The Notifier only knows about the IMessageSender interface! + Notifier(IMessageSender *service, const std::string &recipient) + : sender_(service), recipient_(recipient) {} + + void send(const std::string &message) const { + // The high-level module uses the abstraction (interface). + sender_->sendMessage(recipient_, message); + } + +private: + // Stores a pointer to the generic interface + IMessageSender *sender_; + std::string recipient_; +}; +``` + ## SOLID Only For OOP?