Understanding SOLID Design Principles with Java Code Examples
This article explains the seven SOLID software design principles—Open‑Closed, Dependency Inversion, Single Responsibility, Interface Segregation, Law of Demeter, Liskov Substitution, and Composite/Reuse—illustrating each rule with clear Java code snippets, refactorings, and practical scenarios such as product pricing, purchasing workflows, and database access.
Open‑Closed Principle (OCP)
The OCP states that a software entity (class, module, or function) should be open for extension but closed for modification. By building abstractions for the framework and implementing details in concrete classes, the system becomes more reusable and maintainable. For example, a Goods interface is defined, and a Computer class implements it. To add a discount without changing Computer, a subclass ComputerDiscounts overrides getPrice() and multiplies the original price by 0.88.
package com.ruoyi.demo.designPrinc.ocp;
/** Goods interface */
public interface IGoods {
Integer getId();
String getName();
Double getPrice();
}
/** Concrete product */
public class Computer implements IGoods {
private Integer id;
private String name;
private Double price;
public Computer(Integer id, String name, Double price) {
this.id = id;
this.name = name;
this.price = price;
}
@Override public Integer getId() { return id; }
@Override public String getName() { return name; }
@Override public Double getPrice() { return price; }
}
/** Discount extension */
public class ComputerDiscounts extends Computer {
public ComputerDiscounts(Integer id, String name, Double price) {
super(id, name, price);
}
public Double getOriginalPrice() { return super.getPrice(); }
@Override public Double getPrice() { return super.getPrice() * 0.88; }
}Dependency Inversion Principle (DIP)
DIP requires high‑level modules not to depend on low‑level modules; both should depend on abstractions. The example refactors a Student class that directly calls buyComputer() and buyBook() into a design where Student depends on an IGoods abstraction with a single buy() method. Concrete implementations BuyComputer and BuyBook provide the specific behavior, and the client code passes the desired implementation to Student.buy().
package com.ruoyi.demo.designPrinc.dip;
public interface IGoods {
void buy();
}
public class BuyComputer implements IGoods {
@Override public void buy() { System.out.println("Student bought a computer"); }
}
public class BuyBook implements IGoods {
@Override public void buy() { System.out.println("Student bought a book"); }
}
public class Student {
public void buy(IGoods goods) { goods.buy(); }
}
public class DipTest {
public static void main(String[] args) {
Student student = new Student();
student.buy(new BuyComputer());
student.buy(new BuyBook());
}
}Single Responsibility Principle (SRP)
SRP dictates that a class should have only one reason to change. The initial Goods class handled both self‑operated and agency products with different return policies. By splitting the responsibilities into two concrete classes— ProprietaryGoods and AgentGoods —each class encapsulates a single policy, eliminating the need to modify the class when new policies appear.
package com.ruoyi.demo.designPrinc.srp;
public class ProprietaryGoods {
public void buy(String type) {
System.out.println(type + " requires negotiation for return");
}
}
public class AgentGoods {
public void buy(String type) {
System.out.println(type + " can be returned unconditionally");
}
}
public class SrpTest {
public static void main(String[] args) {
ProprietaryGoods pg = new ProprietaryGoods();
pg.buy("Self‑operated");
AgentGoods ag = new AgentGoods();
ag.buy("Agency");
}
}Interface Segregation Principle (ISP)
ISP advises using multiple fine‑grained interfaces instead of a single bulky one. The original IGoods interface mixed methods for eating, drinking, and wearing, which forced classes like FoodGoods and ClothesGoods to implement irrelevant methods. The refactor introduces three small interfaces— IEatGoods, IDrinkGoods, and IWearGoods —and concrete classes implement only the interfaces they need.
package com.ruoyi.demo.designPrinc.isp;
public interface IEatGoods { void eat(); }
public interface IDrinkGoods { void drink(); }
public interface IWearGoods { void wear(); }
public class FoodGoodsNew implements IEatGoods, IDrinkGoods {
@Override public void eat() { /* eating logic */ }
@Override public void drink() { /* drinking logic */ }
}Law of Demeter (LoD)
LoD (Least Knowledge Principle) recommends that a class should only talk to its immediate friends. In the example, a Boss wants to know how many goods meet a condition. Instead of the Boss directly accessing Goods objects, a Buyer class performs the counting, and the Boss only interacts with Buyer. This reduces coupling between high‑level and low‑level modules.
package com.ruoyi.demo.designPrinc.lod;
public class Goods { }
public class Buyer {
public void checkNumberOfGoods(java.util.List<Goods> goodsList) {
System.out.println("Number of qualified goods: " + goodsList.size());
}
}
public class Boss {
public void commandCheckNumber(Buyer buyer) {
java.util.List<Goods> goodsList = new java.util.ArrayList<>();
for (int i = 0; i < 100; i++) {
goodsList.add(new Goods());
}
buyer.checkNumberOfGoods(goodsList);
}
}
public class LodTest {
public static void main(String[] args) {
Boss boss = new Boss();
Buyer buyer = new Buyer();
boss.commandCheckNumber(buyer);
}
}Liskov Substitution Principle (LSP)
LSP requires that objects of a superclass can be replaced with objects of a subclass without altering program correctness. The article shows a Bird class with a flySpeed field and a getFlyTime() method. A Penguin subclass incorrectly overrides setFlySpeed() to set speed to zero, causing a division‑by‑zero error when the client calculates flight time. The fix is to introduce an Animal base class that defines generic behavior, let Bird inherit from it, and keep Penguin from overriding speed in a way that breaks the contract.
package com.ruoyi.demo.designPrinc.lsp;
public class Bird {
protected double flySpeed;
public double getFlySpeed() { return flySpeed; }
public void setFlySpeed(double speed) { this.flySpeed = speed; }
public double getFlyTime(double distance) { return distance / flySpeed; }
}
public class Swallow extends Bird { }
public class Penguin extends Bird {
@Override public void setFlySpeed(double speed) { this.flySpeed = 0; }
}
public class LspTest {
public static void main(String[] args) {
Bird swallow = new Swallow();
swallow.setFlySpeed(100);
Bird penguin = new Penguin();
penguin.setFlySpeed(100);
try {
System.out.println("Swallow flight time: " + swallow.getFlyTime(50));
System.out.println("Penguin flight time: " + penguin.getFlyTime(50));
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}
}Composite/Aggregate Reuse Principle (CARP)
CARP advises preferring object composition or aggregation over inheritance to achieve reuse. The example starts with a GoodsDao that depends on a concrete DBConnection returning a MySQL connection string. To obey OCP, DBConnection is turned into an abstract class with an abstract getConnection() method. Concrete subclasses MySqlConnection and OracleConnection provide specific connection strings, allowing GoodsDao to work with any database without modification.
package com.ruoyi.demo.designPrinc.carp;
public abstract class DBConnection {
public abstract String getConnection();
}
public class MySqlConnection extends DBConnection {
@Override public String getConnection() { return "MySQL database connection"; }
}
public class OracleConnection extends DBConnection {
@Override public String getConnection() { return "Oracle database connection"; }
}
public class GoodsDao {
private DBConnection dbConnection;
public void setDbConnection(DBConnection dbConnection) { this.dbConnection = dbConnection; }
public void addGoods() {
String connection = dbConnection.getConnection();
System.out.println("Using connection " + connection + " to add goods successfully");
}
}Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
The Dominant Programmer
Resources and tutorials for programmers' advanced learning journey. Advanced tracks in Java, Python, and C#. Blog: https://blog.csdn.net/badao_liumang_qizhi
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
