<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by cs.bites on Medium]]></title>
        <description><![CDATA[Stories by cs.bites on Medium]]></description>
        <link>https://medium.com/@csbites3?source=rss-f76018ed8499------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*T1WW7oyEn2SZqXt1</url>
            <title>Stories by cs.bites on Medium</title>
            <link>https://medium.com/@csbites3?source=rss-f76018ed8499------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 05 Jun 2026 07:42:37 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@csbites3/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Mastering SOLID Principles in C++: The Backbone of Clean Code]]></title>
            <link>https://medium.com/@csbites3/mastering-solid-principles-in-c-the-backbone-of-clean-code-62a87527b9db?source=rss-f76018ed8499------2</link>
            <guid isPermaLink="false">https://medium.com/p/62a87527b9db</guid>
            <dc:creator><![CDATA[cs.bites]]></dc:creator>
            <pubDate>Sun, 21 Sep 2025 10:43:58 GMT</pubDate>
            <atom:updated>2025-09-21T10:43:58.883Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LLW9B7L5R82Mec_4lNdFeA.png" /></figure><p>In software development, writing code that <em>works</em> is only half the job. Writing code that is <strong>maintainable, scalable, and easy to understand</strong> is what sets great developers apart. This is exactly where the <strong>SOLID principles</strong> come in.</p><p>In this article, we’ll break down the SOLID principles one by one, explain them in simple terms, and look at examples (in C++) of how to apply them in real-world projects.</p><p>S — Single responsibility principle (SRP)<br>O — Open/closed principle (OCP)<br>L — Liskov Substitution principle (LSP)<br>I — Interface segregation principle (ISP)<br>D — Dependency inversion principle (DIP)</p><p><strong>Single Responsibility Principle<br></strong><em>A class should have only one reason to change.</em></p><pre>#include &lt;iostream&gt;<br>#include &lt;string&gt;<br>using namespace std;<br><br>class Order {<br>private:<br>    string productName;<br>    int quantity;<br>    double price;<br><br>public:<br>    Order(string p, int q, double pr) : productName(p), quantity(q), price(pr) {}<br><br>    double calculateTotal() {<br>        return quantity * price;<br>    }<br><br>    void processPayment() {<br>        cout &lt;&lt; &quot;Processing payment of $&quot; &lt;&lt; calculateTotal() &lt;&lt; &quot; for &quot; &lt;&lt; productName &lt;&lt; endl;<br>    }<br><br>    void generateInvoice() {<br>        cout &lt;&lt; &quot;Invoice: &quot; &lt;&lt; productName &lt;&lt; &quot; x &quot; &lt;&lt; quantity <br>             &lt;&lt; &quot; = $&quot; &lt;&lt; calculateTotal() &lt;&lt; endl;<br>    }<br>};</pre><p>Here, the Order class is doing multiple things:<br>1. Business logic → calculating total price.<br>2. Payment processing → handling transactions.<br>3. Presentation → generating invoice.</p><p>If payment gateway changes → edit this class.<br>If invoice format changes → edit this class.<br>If pricing rules change → edit this class.<br>This is a violation of SRP, as there as multiple reasons to edit the class.<br>Instead, we can design as below</p><pre>#include &lt;iostream&gt;<br>#include &lt;string&gt;<br>using namespace std;<br><br>// Holds order data only<br>class Order {<br>private:<br>    string productName;<br>    int quantity;<br>    double price;<br><br>public:<br>    Order(string p, int q, double pr) : productName(p), quantity(q), price(pr) {}<br><br>    string getProductName() const { return productName; }<br>    int getQuantity() const { return quantity; }<br>    double getPrice() const { return price; }<br>};<br><br>// Handles order calculations<br>class OrderCalculator {<br>public:<br>    double calculateTotal(const Order&amp; order) {<br>        return order.getQuantity() * order.getPrice();<br>    }<br>};<br><br>// Handles payment processing<br>class PaymentProcessor {<br>public:<br>    void processPayment(const Order&amp; order, double total) {<br>        cout &lt;&lt; &quot;Processing payment of Rs.&quot; &lt;&lt; total <br>             &lt;&lt; &quot; for &quot; &lt;&lt; order.getProductName() &lt;&lt; endl;<br>    }<br>};<br><br>// Handles invoice generation<br>class InvoiceGenerator {<br>public:<br>    void generateInvoice(const Order&amp; order, double total) {<br>        cout &lt;&lt; &quot;Invoice: &quot; &lt;&lt; order.getProductName() &lt;&lt; &quot; x &quot; <br>             &lt;&lt; order.getQuantity() &lt;&lt; &quot; = Rs.&quot; &lt;&lt; total &lt;&lt; endl;<br>    }<br>};</pre><p>Why is this better?<br>Order → only stores data.<br>OrderCalculator → calculates totals.<br>PaymentProcessor → processes payments.<br>InvoiceGenerator → generates invoices.<br>Now, <strong>one class = one responsibility</strong>, making the system flexible, maintainable, and easier to test.</p><p><strong>Open/Closed Principle<br></strong><em>Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification.</em><br>In simple terms, you should be able to add new functionality without modifying the existing code.<br>Why? Because modifying existing code can introduce bugs, break tested functionality, and increase maintenance cost.</p><pre>#include &lt;iostream&gt;<br>using namespace std;<br><br>class Order {<br>public:<br>    double price;<br>    Order(double p) : price(p) {}<br>};<br><br>class Discount {<br>public:<br>    double calculateDiscount(Order&amp; order, string type) {<br>        if (type == &quot;SALE&quot;) {<br>            return order.price * 0.1;  // 10% discount<br>        } else if (type == &quot;VIP&quot;) {<br>            return order.price * 0.2;  // 20% discount<br>        }<br>        return 0;<br>    }<br>};<br><br>int main() {<br>    Order order(100);<br>    Discount discount;<br>    cout &lt;&lt; &quot;Discount: Rs.&quot; &lt;&lt; discount.calculateDiscount(order, &quot;VIP&quot;) &lt;&lt; endl;<br>}</pre><p>Why this violates OCP?<br>- Every time you add a new discount type, you <strong>must modify the </strong><strong>Discount class</strong>.<br>- Existing code changes → risk of breaking tested functionality.</p><pre>#include &lt;iostream&gt;<br>using namespace std;<br><br>class Order {<br>public:<br>    double price;<br>    Order(double p) : price(p) {}<br>};<br><br>// Abstract base class (open for extension)<br>class Discount {<br>public:<br>    virtual double calculate(Order&amp; order) = 0; // pure virtual<br>};<br><br>// New discount types extend the base class<br>class SaleDiscount : public Discount {<br>public:<br>    double calculate(Order&amp; order) override {<br>        return order.price * 0.1;  // 10% discount<br>    }<br>};<br><br>class VIPDiscount : public Discount {<br>public:<br>    double calculate(Order&amp; order) override {<br>        return order.price * 0.2;  // 20% discount<br>    }<br>};<br><br>int main() {<br>    Order order(100);<br><br>    Discount* discount1 = new SaleDiscount();<br>    cout &lt;&lt; &quot;Sale Discount: Rs.&quot; &lt;&lt; discount1-&gt;calculate(order) &lt;&lt; endl;<br><br>    Discount* discount2 = new VIPDiscount();<br>    cout &lt;&lt; &quot;VIP Discount: Rs.&quot; &lt;&lt; discount2-&gt;calculate(order) &lt;&lt; endl;<br>}</pre><p>Why this follows OCP?<br>- Discount class is <strong>closed for modification</strong>.<br>- You can <strong>add new discount types</strong> (e.g., BlackFridayDiscount) by creating a new class that inherits from Discount.<br>- Existing classes (SaleDiscount, VIPDiscount) <strong>don’t need to change</strong>.</p><p><strong>Liskov Substitution Principle<br></strong><em>Objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program.</em><br>In simple terms:<br>- Subclasses should behave in ways that <strong>don’t break the expectations</strong> set by the parent class.<br>- You should be able to <strong>use a subclass anywhere the base class is expected</strong>.</p><pre>#include &lt;iostream&gt;<br>using namespace std;<br><br>class Payment {<br>public:<br>    virtual void pay(double amount) {<br>        cout &lt;&lt; &quot;Paying Rs.&quot; &lt;&lt; amount &lt;&lt; &quot; using generic payment.&quot; &lt;&lt; endl;<br>    }<br>};<br><br>class CreditCardPayment : public Payment {<br>public:<br>    void pay(double amount) override {<br>        cout &lt;&lt; &quot;Paying Rs.&quot; &lt;&lt; amount &lt;&lt; &quot; using Credit Card.&quot; &lt;&lt; endl;<br>    }<br>};<br><br>// Problematic subclass<br>class FreeCouponPayment : public Payment {<br>public:<br>    void pay(double amount) override {<br>        if (amount &gt; 0) {<br>            cout &lt;&lt; &quot;Cannot pay using free coupon for Rs.&quot; &lt;&lt; amount &lt;&lt; endl;<br>        }<br>    }<br>};<br><br>void processPayment(Payment* payment, double amount) {<br>    payment-&gt;pay(amount);<br>}<br><br>int main() {<br>    Payment* payment1 = new CreditCardPayment();<br>    processPayment(payment1, 500);  // Works fine<br><br>    Payment* payment2 = new FreeCouponPayment();<br>    processPayment(payment2, 500);  // Breaks expectations<br>}</pre><p>Why this violates LSP?<br>- processPayment expects <strong>any Payment</strong> to handle the amount.<br>- FreeCouponPayment cannot handle amounts &gt; 0 properly → <strong>breaks the contract of the base class</strong>.<br>- Substituting Payment* with FreeCouponPayment* causes unexpected behavior.</p><pre>#include &lt;iostream&gt;<br>using namespace std;<br><br>class Payment {<br>public:<br>    virtual void pay(double amount) = 0; // pure virtual<br>};<br><br>class CreditCardPayment : public Payment {<br>public:<br>    void pay(double amount) override {<br>        cout &lt;&lt; &quot;Paying Rs.&quot; &lt;&lt; amount &lt;&lt; &quot; using Credit Card.&quot; &lt;&lt; endl;<br>    }<br>};<br><br>class PayPalPayment : public Payment {<br>public:<br>    void pay(double amount) override {<br>        cout &lt;&lt; &quot;Paying Rs.&quot; &lt;&lt; amount &lt;&lt; &quot; using PayPal.&quot; &lt;&lt; endl;<br>    }<br>};<br><br>// FreeCoupon only allows 0 amount<br>class FreeCouponPayment : public Payment {<br>public:<br>    void pay(double amount) override {<br>        if (amount == 0)<br>            cout &lt;&lt; &quot;Using free coupon, amount Rs.&quot; &lt;&lt; amount &lt;&lt; endl;<br>        else<br>            cout &lt;&lt; &quot;Invalid: Free coupon cannot pay Rs.&quot; &lt;&lt; amount &lt;&lt; endl;<br>    }<br>};<br><br>void processPayment(Payment* payment, double amount) {<br>    payment-&gt;pay(amount);<br>}<br><br>int main() {<br>    Payment* payment1 = new CreditCardPayment();<br>    processPayment(payment1, 500);<br><br>    Payment* payment2 = new PayPalPayment();<br>    processPayment(payment2, 800);<br><br>    Payment* payment3 = new FreeCouponPayment();<br>    processPayment(payment3, 0);  // Works as expected<br>}</pre><p>Here,<br>- Every subclass can <strong>replace the base class</strong> without breaking expectations.<br>- processPayment works correctly for <strong>all Payment objects</strong>.<br>- Contract of the base class (pay) is respected.<br>LSP ensures your subclasses are true “substitutes” for their parent class. Violating LSP often leads to <strong>unexpected behavior and bugs</strong> in polymorphic code.</p><p><strong>Interface Segregation Principle</strong><br><em>Clients should not be forced to depend on interfaces they do not use.</em><br>A <strong>client</strong> is any class, function, or module that <strong>calls methods or accesses functionality</strong> of another class/interface.<br>In simple terms:<br>- Instead of creating a <strong>large, bloated interface</strong>, break it into <strong>smaller, specific interfaces</strong>.<br>- Classes should implement only the methods they actually need.</p><pre>#include &lt;iostream&gt;<br>using namespace std;<br><br>class ECommerceMachine {<br>public:<br>    virtual void buyProduct() = 0;<br>    virtual void returnProduct() = 0;<br>    virtual void giftProduct() = 0;  // Not every machine supports this<br>};<br><br>class SimpleStore : public ECommerceMachine {<br>public:<br>    void buyProduct() override {<br>        cout &lt;&lt; &quot;Buying product...&quot; &lt;&lt; endl;<br>    }<br><br>    void returnProduct() override {<br>        cout &lt;&lt; &quot;Returning product...&quot; &lt;&lt; endl;<br>    }<br><br>    void giftProduct() override {<br>        // Problem: SimpleStore cannot gift, but forced to implement<br>        cout &lt;&lt; &quot;Cannot gift product!&quot; &lt;&lt; endl;<br>    }<br>};</pre><p>Here,<br>- SimpleStore is forced to implement giftProduct() even though it doesn’t support it.<br>- Large interfaces → unnecessary implementations, more maintenance, potential bugs.</p><p>Instead, we split the interface into <strong>smaller, focused interfaces</strong>:</p><pre>#include &lt;iostream&gt;<br>using namespace std;<br><br>// Core buying interface<br>class Buyable {<br>public:<br>    virtual void buyProduct() = 0;<br>};<br><br>// Optional return interface<br>class Returnable {<br>public:<br>    virtual void returnProduct() = 0;<br>};<br><br>// Optional gift interface<br>class Giftable {<br>public:<br>    virtual void giftProduct() = 0;<br>};<br><br>// Simple store only supports buying and returning<br>class SimpleStore : public Buyable, public Returnable {<br>public:<br>    void buyProduct() override {<br>        cout &lt;&lt; &quot;Buying product...&quot; &lt;&lt; endl;<br>    }<br><br>    void returnProduct() override {<br>        cout &lt;&lt; &quot;Returning product...&quot; &lt;&lt; endl;<br>    }<br>};<br><br>// Premium store supports all actions<br>class PremiumStore : public Buyable, public Returnable, public Giftable {<br>public:<br>    void buyProduct() override {<br>        cout &lt;&lt; &quot;Buying product...&quot; &lt;&lt; endl;<br>    }<br><br>    void returnProduct() override {<br>        cout &lt;&lt; &quot;Returning product...&quot; &lt;&lt; endl;<br>    }<br><br>    void giftProduct() override {<br>        cout &lt;&lt; &quot;Gifting product...&quot; &lt;&lt; endl;<br>    }<br>};</pre><p>So,<br>- Each class <strong>implements only the interfaces it needs</strong>.<br>- No unnecessary methods → easier to maintain, fewer bugs.<br>- Clients are not forced to depend on functionality they don’t use.</p><p><strong>Dependency Inversion Principle</strong><br><em>High-level modules should not depend on low-level modules. Both should depend on abstractions (interfaces). Abstractions should not depend on details. Details should depend on abstractions.</em></p><p>In simple terms,<br>- Don’t make your high-level code depend on concrete implementations.<br>- Depend on <strong>interfaces or abstract classes</strong> so you can change low-level details without touching high-level logic.</p><p>Consider an <strong>order notification system</strong>:</p><pre>#include &lt;iostream&gt;<br>using namespace std;<br><br>// Low-level module<br>class EmailService {<br>public:<br>    void sendEmail(string message) {<br>        cout &lt;&lt; &quot;Sending email: &quot; &lt;&lt; message &lt;&lt; endl;<br>    }<br>};<br><br>// High-level module<br>class OrderProcessor {<br>private:<br>    EmailService emailService;  // Direct dependency<br>public:<br>    void processOrder() {<br>        cout &lt;&lt; &quot;Processing order...&quot; &lt;&lt; endl;<br>        emailService.sendEmail(&quot;Order processed successfully&quot;);<br>    }<br>};<br><br>int main() {<br>    OrderProcessor processor;<br>    processor.processOrder();<br>}</pre><p>Here,<br>- OrderProcessor (high-level) depends <strong>directly</strong> on EmailService (low-level).<br>- What if we want to send SMS instead? Or push notification?<br>- We’d have to <strong>modify </strong><strong>OrderProcessor</strong>, which is bad.</p><p>Instead, we introduce abstraction (interface) for notifications:</p><pre>#include &lt;iostream&gt;<br>using namespace std;<br><br>// Abstraction<br>class INotifier {<br>public:<br>    virtual void notify(string message) = 0;<br>};<br><br>// Low-level module 1<br>class EmailNotifier : public INotifier {<br>public:<br>    void notify(string message) override {<br>        cout &lt;&lt; &quot;Sending email: &quot; &lt;&lt; message &lt;&lt; endl;<br>    }<br>};<br><br>// Low-level module 2<br>class SMSNotifier : public INotifier {<br>public:<br>    void notify(string message) override {<br>        cout &lt;&lt; &quot;Sending SMS: &quot; &lt;&lt; message &lt;&lt; endl;<br>    }<br>};<br><br>// High-level module depends on abstraction<br>class OrderProcessor {<br>private:<br>    INotifier* notifier;  // depends on interface<br>public:<br>    OrderProcessor(INotifier* n) : notifier(n) {}<br><br>    void processOrder() {<br>        cout &lt;&lt; &quot;Processing order...&quot; &lt;&lt; endl;<br>        notifier-&gt;notify(&quot;Order processed successfully&quot;);<br>    }<br>};<br><br>int main() {<br>    INotifier* emailNotifier = new EmailNotifier();<br>    OrderProcessor processor1(emailNotifier);<br>    processor1.processOrder();<br><br>    INotifier* smsNotifier = new SMSNotifier();<br>    OrderProcessor processor2(smsNotifier);<br>    processor2.processOrder();<br>}</pre><p>Now,<br>- OrderProcessor (high-level) <strong>depends only on the abstraction</strong> INotifier.<br>- You can now add new notification types (push, WhatsApp, etc.) <strong>without changing </strong><strong>OrderProcessor.<br>- </strong>Low-level modules (EmailNotifier, SMSNotifier) <strong>depend on the same abstraction</strong>.</p><p>DIP decouples high-level logic from low-level implementation, making your code <strong>flexible, testable, and easy to extend</strong>.</p><p>Next time you design a class, ask yourself — is it SOLID?</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=62a87527b9db" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>