<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Phùng Hữu Đại Khánh</title>
    <description>The latest articles on DEV Community by Phùng Hữu Đại Khánh (@khapu2906).</description>
    <link>https://dev.to/khapu2906</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F459745%2F8ae6c506-691f-48d5-89a4-0ad11e004519.jpg</url>
      <title>DEV Community: Phùng Hữu Đại Khánh</title>
      <link>https://dev.to/khapu2906</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/khapu2906"/>
    <language>en</language>
    <item>
      <title>OOP Beyond the Four Pillars: State, Behavior, and Invariants</title>
      <dc:creator>Phùng Hữu Đại Khánh</dc:creator>
      <pubDate>Thu, 16 Jul 2026 11:04:06 +0000</pubDate>
      <link>https://dev.to/khapu2906/the-real-essence-of-oop-state-behavior-and-invariants-2k1d</link>
      <guid>https://dev.to/khapu2906/the-real-essence-of-oop-state-behavior-and-invariants-2k1d</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;1. From Procedural to Object-Oriented&lt;/li&gt;
&lt;li&gt;2. What Problem Does OOP Actually Solve?&lt;/li&gt;
&lt;li&gt;3. Encapsulation — Access Control, Not Lifecycle Management&lt;/li&gt;
&lt;li&gt;4. Inheritance — A Tool for Substitutability, Not a Default Fix for Duplication&lt;/li&gt;
&lt;li&gt;5. Polymorphism — The Core Is Subtype Polymorphism, Not Overloading&lt;/li&gt;
&lt;li&gt;6. Abstraction — Much Broader Than an Interface&lt;/li&gt;
&lt;li&gt;7. Behavior-State Binding and Invariants&lt;/li&gt;
&lt;li&gt;8. The Cost of OOP&lt;/li&gt;
&lt;li&gt;OOP Is About Responsibility, Not Classes&lt;/li&gt;
&lt;li&gt;A Note on Rich vs. Anemic Models&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When object-oriented programming (OOP) comes up, most people immediately list: Encapsulation, Inheritance, Polymorphism, Abstraction. But reciting four names doesn't mean understanding the substance — and worse, a lot of popular material explains these four properties in ways that are conceptually misleading.&lt;/p&gt;

&lt;p&gt;Code examples below are written in Java, purely as notation — the ideas are the point, not the language.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. From Procedural to Object-Oriented
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Procedural: data and behavior live apart
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Bank&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Global/static state&lt;/span&gt;

    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;withdraw&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;balance&lt;/code&gt; is a static variable — any part of the program can read or write it directly, with no guarantee it's ever checked, so the data can end up in an invalid state at any time.&lt;/p&gt;

&lt;h3&gt;
  
  
  OOP: data bound to the behavior that protects it
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;(examples below use &lt;code&gt;java.math.BigDecimal&lt;/code&gt; — the standard choice for money in Java, since &lt;code&gt;double&lt;/code&gt; accumulates rounding error that a real invariant check can't tolerate)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BankAccount&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;BankAccount&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;initial&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initial&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;signum&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Initial balance cannot be negative"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;initial&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;withdraw&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;signum&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compareTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;subtract&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="nf"&gt;getBalance&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The point is not "hiding &lt;code&gt;balance&lt;/code&gt;."&lt;/strong&gt; The point is that &lt;code&gt;BankAccount&lt;/code&gt; exists to guarantee one rule always holds — &lt;code&gt;balance &amp;gt;= 0&lt;/code&gt; — no matter who calls what, in what order. An object isn't a box that stores data. &lt;strong&gt;One of the most important roles of an object is protecting invariants.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the starting point the rest of the article builds on.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. What Problem Does OOP Actually Solve?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem in Procedural Code&lt;/th&gt;
&lt;th&gt;OOP Mechanism&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;State can be mutated arbitrarily, with no guardrails&lt;/td&gt;
&lt;td&gt;Encapsulation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logic duplicated across variants of the same concept&lt;/td&gt;
&lt;td&gt;Composition / Inheritance (conditionally)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Calling code has to know the concrete type it's handling&lt;/td&gt;
&lt;td&gt;Polymorphism&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;System complexity makes it hard to separate levels of detail&lt;/td&gt;
&lt;td&gt;Abstraction&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Inheritance is no longer the default answer to "code duplication"&lt;/strong&gt; — the reason follows in section 4.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Encapsulation — Access Control, Not Lifecycle Management
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The correct definition
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Encapsulation means controlling access to state and guaranteeing an invariant holds&lt;/strong&gt;, by only allowing state changes through a controlled set of behaviors (methods).&lt;/p&gt;

&lt;h3&gt;
  
  
  Encapsulation is not the same thing as lifecycle management
&lt;/h3&gt;

&lt;p&gt;A common conflation in OOP teaching is treating Encapsulation as if it also covers managing an object's memory or resource lifetime. It doesn't — they're different responsibilities that some languages happen to bind to the same syntax. Encapsulation is about &lt;strong&gt;who can touch state and under what conditions&lt;/strong&gt;; lifecycle management is about &lt;strong&gt;how long a resource is held and when it gets released&lt;/strong&gt;. An object can have one without the other: most objects, like &lt;code&gt;BankAccount&lt;/code&gt; above, never own an external resource at all — &lt;code&gt;private&lt;/code&gt; fields and validated methods are the whole story, no lifecycle concern in sight.&lt;/p&gt;

&lt;p&gt;Lifecycle management only becomes relevant for the minority of objects holding something outside the object itself — a file handle, a socket, a database connection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FileLogger&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;AutoCloseable&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;BufferedWriter&lt;/span&gt; &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;FileLogger&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BufferedWriter&lt;/span&gt; &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;writer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;IOException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// resource cleanup — nothing to do with access control&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;AutoCloseable&lt;/code&gt;/&lt;code&gt;close()&lt;/code&gt; exist purely to release the resource; they say nothing about who can read or write the object's state. Notice this is a separate mechanism from &lt;code&gt;private&lt;/code&gt;/getters/setters entirely — a good illustration that Encapsulation and lifecycle management are independent concerns, not two faces of the same thing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bottom line:&lt;/strong&gt; Encapsulation is a universal OOP concept that applies to every object. Lifecycle/resource management is a narrower, separate concern that only shows up when an object actually owns an external resource.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Inheritance — A Tool for Substitutability, Not a Default Fix for Duplication
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Overriding relies on dynamic dispatch
&lt;/h3&gt;

&lt;p&gt;Overriding only means something if the call is resolved by the object's actual type at runtime, not by the variable's declared type at compile time. That's what makes this work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Animal&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;speak&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Animal sound"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Dog&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Animal&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;speak&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Woof!"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Animal&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Dog&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;speak&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Prints "Woof!" — resolved by the actual object, not the declared type&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;@Override&lt;/code&gt; here isn't what makes overriding happen — it's just an annotation that lets the compiler &lt;strong&gt;flag an error if you think you're overriding but actually got the signature wrong&lt;/strong&gt; (e.g., a typo'd method name). It's a safety net for catching mistakes, not the mechanism itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inheritance is not the default tool for "reusing logic"
&lt;/h3&gt;

&lt;p&gt;The common guideline in modern design:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Favor Composition over Inheritance&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The classic headache:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AdminUser&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SuperAdminUser&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AdminUser&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Say &lt;code&gt;AdminUser&lt;/code&gt; can do everything a regular user can, plus manage other users. &lt;code&gt;SuperAdminUser&lt;/code&gt; adds billing access on top. That much fits a linear hierarchy fine. The trouble starts the moment &lt;code&gt;AdminUser&lt;/code&gt; also needs a capability that lives on a completely different, unrelated branch — say, the same &lt;em&gt;view-only, read-restricted&lt;/em&gt; mode that a hypothetical &lt;code&gt;ReadOnlyUser&lt;/code&gt; has. There's no clean way to "borrow" that behavior sideways: &lt;code&gt;AdminUser&lt;/code&gt; can't extend two classes, and forcing &lt;code&gt;ReadOnlyUser&lt;/code&gt; into the same chain just to share one capability distorts the whole hierarchy for everyone else using it.&lt;/p&gt;

&lt;p&gt;Composition sidesteps this entirely, because capabilities become objects you attach rather than branches you inherit from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Permissions&lt;/span&gt; &lt;span class="n"&gt;permissions&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// just a set of granted actions&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;User&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Permissions&lt;/span&gt; &lt;span class="n"&gt;permissions&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;permissions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;permissions&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;can&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Action&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;permissions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;allows&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Any combination of capabilities can be composed freely — no hierarchy to fight:&lt;/span&gt;
&lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Permissions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MANAGE_USERS&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;VIEW_ONLY&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;superAdmin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Permissions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MANAGE_USERS&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BILLING&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;VIEW_ONLY&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;admin&lt;/code&gt; and &lt;code&gt;superAdmin&lt;/code&gt; aren't different &lt;em&gt;classes&lt;/em&gt; anymore — they're the same &lt;code&gt;User&lt;/code&gt; class configured with a different &lt;code&gt;Permissions&lt;/code&gt; object. Adding "view-only" to &lt;code&gt;AdminUser&lt;/code&gt; is no longer a hierarchy problem; it's just handing it a different set of permissions at construction time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inheritance is only safe when the Liskov Substitution Principle holds
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;"is-a" relationship&lt;/strong&gt; by itself isn't the real test — plenty of things sound like "is-a" in English but break down in code. The actual condition is the &lt;strong&gt;Liskov Substitution Principle (LSP)&lt;/strong&gt;: anywhere the base class is used, substituting the subclass must work correctly without the caller needing to know it's a subclass. LSP is the reason Inheritance is trustworthy at all when it is; without it, "is-a" is just a feeling, not a guarantee.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Dog extends Animal&lt;/code&gt; holds up under this test — a &lt;code&gt;Dog&lt;/code&gt; behaves as an &lt;code&gt;Animal&lt;/code&gt; everywhere an &lt;code&gt;Animal&lt;/code&gt; is expected. Inheriting just to "borrow a few existing methods," without that substitutability guarantee, is a signal to switch to composition instead.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Polymorphism — The Core Is Subtype Polymorphism, Not Overloading
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Overloading is ad-hoc polymorphism — useful, but not the core
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MathUtil&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is resolved at compile time and has nothing to do with dynamic dispatch — it's just syntactic convenience.&lt;/p&gt;

&lt;h3&gt;
  
  
  The real power: subtype polymorphism
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Vehicle&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Car&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Vehicle&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Car moves"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Bike&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Vehicle&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bike moves"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Fleet&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;moveAll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Vehicle&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;vehicles&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Vehicle&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;vehicles&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;move&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// No need to know whether it's a Car or a Bike&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the essence: &lt;strong&gt;the same call site (&lt;code&gt;v.move()&lt;/code&gt;) dispatches to different behavior at runtime, based on the object's concrete type&lt;/strong&gt; — &lt;code&gt;Fleet&lt;/code&gt; needs no changes when a &lt;code&gt;Truck&lt;/code&gt; or &lt;code&gt;Boat&lt;/code&gt; is added.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Polymorphism lets code handle a set of different types &lt;strong&gt;through the same abstraction&lt;/strong&gt;, without needing to know — or care — what the concrete type behind it is.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  6. Abstraction — Much Broader Than an Interface
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Interfaces and abstract classes are tools, not Abstraction itself
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Shape&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;area&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Circle&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Shape&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Circle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;area&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example is &lt;strong&gt;correct but incomplete&lt;/strong&gt; — it only illustrates one specific form called &lt;strong&gt;interface abstraction&lt;/strong&gt;. A pure contract with no state (&lt;code&gt;interface&lt;/code&gt;) and a partial implementation that can hold state (&lt;code&gt;abstract class&lt;/code&gt;) are two different tools for achieving Abstraction — but neither one &lt;em&gt;is&lt;/em&gt; Abstraction itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Abstraction actually is
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserRepository&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// The caller doesn't need to know:&lt;/span&gt;
        &lt;span class="c1"&gt;// - SQL or NoSQL&lt;/span&gt;
        &lt;span class="c1"&gt;// - how transactions are opened/closed&lt;/span&gt;
        &lt;span class="c1"&gt;// - how the connection pool is configured&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;repository.save(user)&lt;/code&gt; is a complete abstraction even without any &lt;code&gt;interface&lt;/code&gt; — as long as the caller doesn't need the internal details. Abstraction lives in &lt;strong&gt;the level of detail exposed through a function's name and parameters&lt;/strong&gt;, not in whether &lt;code&gt;interface&lt;/code&gt;/&lt;code&gt;abstract&lt;/code&gt; is used.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In short:&lt;/strong&gt; &lt;code&gt;interface&lt;/code&gt;/&lt;code&gt;abstract class&lt;/code&gt; are &lt;em&gt;tools&lt;/em&gt; for achieving Abstraction, especially useful when multiple interchangeable implementations are needed. But Abstraction, as a design principle, exists at every layer of code — including a plain &lt;code&gt;private&lt;/code&gt; method.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Behavior-State Binding and Invariants
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BankAccount&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;BankAccount&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;initialBalance&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initialBalance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;signum&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Initial balance cannot be negative"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;initialBalance&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;withdraw&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;signum&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compareTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;subtract&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="nf"&gt;getBalance&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important question isn't "what does this class store?" It's:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What rule does this class protect, that must always hold no matter the sequence of operations?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For &lt;code&gt;BankAccount&lt;/code&gt;, that rule is &lt;code&gt;balance &amp;gt;= 0&lt;/code&gt; — enforced not just in &lt;code&gt;withdraw&lt;/code&gt;, but in the constructor too, since an invariant that only holds &lt;em&gt;after&lt;/em&gt; the first call isn't really an invariant. The entire design — &lt;code&gt;private&lt;/code&gt;, the condition checks — exists solely to serve that one rule.&lt;/p&gt;

&lt;p&gt;A natural follow-up question: does &lt;code&gt;getBalance()&lt;/code&gt; returning the raw value undercut any of this? Not inherently — exposing read-only state isn't a violation by itself. What matters is &lt;em&gt;where decisions based on that state get made&lt;/em&gt;. &lt;code&gt;getBalance()&lt;/code&gt; lets a caller look; &lt;code&gt;withdraw()&lt;/code&gt; is still the only way to change anything, and it's still the one place enforcing the rule. Exposing a value for reading and protecting the rules that govern changing it are two separate questions.&lt;/p&gt;

&lt;p&gt;Protecting invariants is one of the most important responsibilities many domain objects have. In practice, this is why OOP often emphasizes keeping behavior close to the state it governs.&lt;/p&gt;

&lt;p&gt;A related concept is identity.&lt;/p&gt;

&lt;p&gt;Two objects may contain identical state and behavior while still represent different entities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Customer&lt;/span&gt; &lt;span class="n"&gt;alice1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Alice"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;Customer&lt;/span&gt; &lt;span class="n"&gt;alice2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Alice"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For that reason, many classic OOP definitions describe an object as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Identity + State + Behavior&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This article focuses primarily on responsibility and invariant protection, but identity remains an important part of object-oriented thinking.&lt;/p&gt;

&lt;h3&gt;
  
  
  Not All Invariants Belong to a Single Object
&lt;/h3&gt;

&lt;p&gt;The examples in this article focus on invariants protected by a single object.&lt;/p&gt;

&lt;p&gt;Real systems often contain rules that span multiple objects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An order cannot be shipped unless it has been paid.&lt;/li&gt;
&lt;li&gt;Inventory cannot become negative.&lt;/li&gt;
&lt;li&gt;A bank transfer must debit one account and credit another.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Protecting such rules often requires coordination beyond a single object and is one of the motivations behind concepts such as Aggregates in Domain-Driven Design. An Aggregate defines a consistency boundary within which invariants are guaranteed to hold.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. The Cost of OOP
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deep inheritance hierarchies&lt;/strong&gt;: the more &lt;code&gt;extends&lt;/code&gt; layers, the harder to test and refactor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anemic Domain Model&lt;/strong&gt;: extremely common in Spring-style Java codebases. A class ends up as nothing but fields and getters/setters:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

      &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getEmail&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setEmail&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// no validation, no rule&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anywhere that needs to change an email now has to duplicate the validation logic itself (&lt;code&gt;UserService&lt;/code&gt;, a controller, a batch job — whoever gets there first), because &lt;code&gt;User&lt;/code&gt; enforces nothing. Compare that to keeping the rule where the state lives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

      &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;changeEmail&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;newEmail&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;newEmail&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"@"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
              &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid email"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
          &lt;span class="o"&gt;}&lt;/span&gt;
          &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newEmail&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;setEmail&lt;/code&gt; just stores whatever it's given — the object protects no invariant, so it isn't really acting as an object in the OOP sense, just a data bag with a class-shaped label. &lt;code&gt;changeEmail&lt;/code&gt; puts the rule back where it belongs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Overusing Inheritance&lt;/strong&gt; for code reuse instead of modeling a genuine "is-a" relationship.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interface explosion&lt;/strong&gt; — creating an &lt;code&gt;interface&lt;/code&gt; for everything "just in case" before there's an actual need (a violation of YAGNI) — a fairly common habit in the Java/Spring ecosystem.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A counterbalancing principle worth knowing: &lt;strong&gt;Tell, Don't Ask&lt;/strong&gt; — instead of &lt;code&gt;if (account.getBalance().compareTo(amount) &amp;gt;= 0) { ... }&lt;/code&gt; followed by external handling, call &lt;code&gt;account.withdraw(amount)&lt;/code&gt; so the decision logic stays close to the data it governs.&lt;/p&gt;




&lt;h2&gt;
  
  
  OOP Is About Responsibility, Not Classes
&lt;/h2&gt;

&lt;p&gt;A useful test for whether a codebase is actually object-oriented, or just uses &lt;code&gt;class&lt;/code&gt; as a file-organization convention: pick a business rule and ask &lt;strong&gt;which object owns it&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;User&lt;/span&gt;
&lt;span class="nc"&gt;UserService&lt;/span&gt;
&lt;span class="nc"&gt;UserValidator&lt;/span&gt;
&lt;span class="nc"&gt;UserMapper&lt;/span&gt;
&lt;span class="nc"&gt;UserHelper&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Five classes, one entity. If the email-format rule from the example above lives in &lt;code&gt;UserValidator&lt;/code&gt;, the discount calculation lives in &lt;code&gt;UserService&lt;/code&gt;, and &lt;code&gt;User&lt;/code&gt; itself is just the anemic getter/setter bag — then no single object owns any rule. The rules float between service and helper classes that operate &lt;em&gt;on&lt;/em&gt; data rather than objects that are &lt;em&gt;responsible for&lt;/em&gt; their own data. That's a procedural program wearing class syntax, not OOP.&lt;/p&gt;

&lt;p&gt;The fix isn't to delete &lt;code&gt;UserService&lt;/code&gt; — coordinating multiple objects across a use case is a legitimate job for a service layer. The fix is narrower: &lt;strong&gt;the rule that only concerns &lt;code&gt;User&lt;/code&gt;'s own state&lt;/strong&gt; (is this email valid, can this balance go negative) belongs on &lt;code&gt;User&lt;/code&gt;, not scattered across whichever class happened to need it first. Everything in sections 3 and 7 — encapsulation as invariant protection, behavior bound to state — comes down to this one habit: before writing a rule, ask which object it actually belongs to.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Note on Rich vs. Anemic Models
&lt;/h2&gt;

&lt;p&gt;The examples in this article intentionally focus on objects that own and enforce their own invariants.&lt;/p&gt;

&lt;p&gt;That does not mean every architecture must place every business rule inside an entity.&lt;/p&gt;

&lt;p&gt;The important question is not where a piece of code physically lives, but which part of the system is responsible for maintaining correctness.&lt;/p&gt;

&lt;p&gt;Responsibility matters more than placement.&lt;/p&gt;

&lt;p&gt;CRUD applications, reporting systems, data pipelines,&lt;br&gt;
and some functional architectures intentionally separate&lt;br&gt;
data from behavior.&lt;/p&gt;

&lt;p&gt;Anemic models become a problem only when invariants&lt;br&gt;
and business rules end up scattered with no clear owner.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Core Essence&lt;/th&gt;
&lt;th&gt;Not to Be Confused With&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Encapsulation&lt;/td&gt;
&lt;td&gt;Access control + invariant protection&lt;/td&gt;
&lt;td&gt;Lifecycle/resource management (&lt;code&gt;close()&lt;/code&gt;, GC)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inheritance&lt;/td&gt;
&lt;td&gt;Subtype substitutability (LSP)&lt;/td&gt;
&lt;td&gt;The default tool for code reuse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Polymorphism&lt;/td&gt;
&lt;td&gt;Runtime dispatch through a shared abstraction&lt;/td&gt;
&lt;td&gt;Overloading (ad-hoc polymorphism)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Abstraction&lt;/td&gt;
&lt;td&gt;Hiding detail at the right level&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;interface&lt;/code&gt;/&lt;code&gt;abstract class&lt;/code&gt; (just a tool)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;None of this is really about Java — swap in any language with dynamic dispatch and a garbage collector and the same points hold.&lt;br&gt;
The four properties above are useful vocabulary, but they are not the essence of OOP.&lt;br&gt;
The deeper idea behind object-oriented programming is responsibility.&lt;br&gt;
An object owns identity and state, exposes behavior, and protects the invariants that define its correctness. Some invariants belong to individual objects, while others belong to larger parts of the system. The role of design is to place each responsibility where it can be protected most reliably.&lt;br&gt;
Encapsulation, Inheritance, Polymorphism, and Abstraction are valuable tools, but they matter only insofar as they help responsibilities stay in the right place and correctness remain protected.&lt;br&gt;
To go deeper: SOLID principles, Domain-Driven Design (DDD), Dependency Inversion, and Rich Domain Model vs. Anemic Domain Model.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>oop</category>
      <category>programming</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
