Hi adventurers! Here I will discuss some fundamental principles, also known as SOLID, that will help you build you own solid, rich and wealthy kingdom 👑.

WHAT is SOLID and WHY do you need it?
SOLID stands for 5 distinct principles, each one given it’s own letter in the SOLID acronym:
- Single responsibility principle
- Open-closed principle
- Liskov substitution principle
- Interface segregation principle
- Dependency inversion principle
So… In a realm where dragons soar and magic thrives, even the greatest of kingdoms can crumble if not built upon a solid foundation. Just as a dragon’s scales protect it from harm, the SOLID principles serve as a shield for your software architecture, ensuring it can withstand the test of time and the fiery breath of change.
Managing SOLID principles will ensure your code kingdom remains solid for centuries. As everything starts with basics, building your own program (or a kingdom) starts with leveraging these 5 simple, but very powerful principles.
Let’s begin our journey to discover these old secrets, handed down by generations of wise old wizards and master craftspeople!
S – Single Responsibility Principle (SRP): A Focused Court
As a King or a Queen of your kingdom, when you look at your court members, each member of the royal court has a specific role — the advisor offers counsel, the blacksmith forges weapons, and the knight defends the realm.
Similarly, each component of your kingdom’s infrastructure (be it a castle, a road, or a law) should have a single, well-defined purpose. When a structure serves multiple, unrelated functions, it becomes difficult to maintain and adapt to changing needs. A castle that doubles as a barracks and a granary, for example, could face logistical challenges and vulnerabilities.
Let’s analyse the next ancient lines describing the SRP principle (all the code snippets will be in Java, but the main idea should be understandable to anyone familiar with at least one programming language to some extant):
// Responsible for overall castle structure and management
public class Castle {
private String name;
private Double size;
private Integer defenseLevel;
private Treasury treasury;
private GuardTower guardTower;
public void defend() {
guardTower.raiseAlarm();
// Additional defense logic
}
public void upgradeDefense() {
if (treasury.hasSufficientGold()) {
defenseLevel++;
treasury.withdrawGold(500);
} else {
System.out.println("Not enough gold for an upgrade!");
}
}
}
As you can see from the above lines, the castle is only responsible for overall structure and management, and does not take the responsibilities of the Treasury and the Guard Tower, letting them deal with any related problems by themselves:
// Responsible for managing the castle's gold reserves
public class Treasury {
private int goldAmount;
public boolean hasSufficientGold() {
return goldAmount >= 500;
}
public void withdrawGold(int amount) {
goldAmount -= amount;
}
}
// Responsible for raising alarms and guard activities
public class GuardTower {
public void raiseAlarm() {
System.out.println("Alarm! Enemies approaching!");
// Additional alarm logic
}
That’s what an SRP principle tells us — each component should only be responsible for one thing, and thus everyone will know exactly what they should do. Noone will be misunderstood or confused, overwhelmed with the ever ending lists of things to do, and your kingdom will thrive. Divide et impera!
Benefits
- Maintainability: easier to modify or fix one aspect of the code without affecting others.
- Testability: you can easily write unit tests for each class in isolation.
- Reusability: the individual classes are more likely to be reusable in other contexts.
O — The Open/Closed Principle (OCP): The Ever-Expanding Castle
A castle, like a kingdom, should be open to expansion but closed to major modifications. Adding new towers and wings should not necessitate tearing down existing structures (except when conquering new kingdoms of course, but for now we talk about your kingdom).
This principle applies to everything from your laws and customs to the layout of your settlements. By designing for flexibility and extension, you ensure that your kingdom can evolve without sacrificing its foundation. Let’s see what the ancient scripts say.
Your castle will definitely have various defense mechanisms. You want to be able to add new defenses without rewriting the core castle logic. For now, you have the archer towers and some boiling oil pots as your castle defenses:
// Defense interface
interface DefenseMechanism {
void defend();
}
// You may want to have any number of those
class ArcherTower implements DefenseMechanism {
@Override
public void defend() {
System.out.println("Archers firing arrows!");
}
}
// And these, too!
class BoilingOilPit implements DefenseMechanism {
@Override
public void defend() {
System.out.println("Pouring boiling oil!");
}
}
But what if you would like your dragon (of course you have one, you are the true Targaryen heir!) to defend your castle, too? Following the OCP, you would do something like this:
// Added without modifying existing defenses
class Dragon implements DefenseMechanism {
@Override
public void defend() {
System.out.println("Dragon breathes fire!");
}
}
Now, when you have all your defenses up, let’s test them!
class Castle {
private List<DefenseMechanism> defenses = new ArrayList<>();
public void addDefense(DefenseMechanism defense) {
defenses.add(defense);
}
public void defend() {
for (DefenseMechanism defense : defenses) {
defense.defend();
}
}
}
// Usage
public class Main {
public static void main(String[] args) {
Castle castle = new Castle();
castle.addDefense(new ArcherTower());
castle.addDefense(new BoilingOilPit());
// Add new defense (Open for Extension)
castle.addDefense(new Dragon()); // No modification to Castle or existing defenses
castle.defend();
}
}
We extend the castle’s capabilities by adding a new defense mechanism (open for extension) without altering the existing Castle or its defenses (closed for modification). With this strategy, you will be able add any types of defenses, saving your castle’s epic look and existing defense lines (if, of course, your can restrain the temper of your dragon).
Benefits
- Flexibility: you can easily add new defenses in the future.
- Maintainability: the code is easier to understand and modify.
- Reduced Risk: changes are less likely to introduce bugs in existing functionality.
L — The Liskov Substitution Principle (LSP): The Royal Lineage
The royal lineage ensures a smooth succession of power. The heir to the throne must be able to fulfil the duties of the monarch without disrupting the kingdom’s functioning (Long live Queen Rhaenyra!).
This concept applies to all aspects of governance. A new law, for instance, should be able to replace an old one without causing chaos or requiring a complete overhaul of existing systems. Let’s see it on an example of your future heirs.
Different members of a royal lineage should be able to fulfill the roles expected of their positions without causing unexpected behavior. You, as a ruler of your kingdom, are represented as a Monarch:
// RoyalMember Interface
interface RoyalMember {
String getTitle();
void performDuties();
}
// Royal Member Implementations
class Monarch implements RoyalMember {
@Override
public String getTitle() {
return "Monarch";
}
@Override
public void performDuties() {
System.out.println("Governing the kingdom, leading ceremonies, and making decisions.");
}
}
Your future heirs will also be of royal blood:
class HeirApparent implements RoyalMember {
@Override
public String getTitle() {
return "Heir Apparent";
}
@Override
public void performDuties() {
System.out.println("Preparing for the throne, attending events, and representing the monarch.");
}
}
class PrincePrincess implements RoyalMember {
@Override
public String getTitle() {
return "Prince/Princess";
}
@Override
public void performDuties() {
System.out.println("Supporting the monarch, attending charitable events, and fulfilling royal engagements.");
}
}
And of course they need to be announced as you are, with all the respect to Royalty:
// Royal Lineage Class
class RoyalLineage {
private List<RoyalMember> members = new ArrayList<>();
public void addMember(RoyalMember member) {
members.add(member);
}
public void announceMembers() {
for (RoyalMember member : members) {
System.out.println(member.getTitle() + ": " + member.performDuties());
}
}
}
// Usage
public class Main {
public static void main(String[] args) {
RoyalLineage lineage = new RoyalLineage();
lineage.addMember(new Monarch());
lineage.addMember(new HeirApparent());
lineage.addMember(new PrincePrincess());
lineage.announceMembers();
}
}
When it’s this way, you can be sure your heirs will be well prepared for the throne after you!
Benefits
- Subtype Compatibility: each royal member type can be substituted for the
RoyalMember
interface. - Behavioral Consistency: the kingdom works correctly regardless of the specific type of royal member.
- Flexibility: you can easily add new types of royal members (e.g., Duke/Duchess) as long as they implement the
RoyalMember
interface.
I — The Interface Segregation Principle (ISP): The Versatile Craftsman
A skilled craftsman specializes in a particular trade but often possesses knowledge and skills in related fields. This diversity allows them to contribute to various projects without being overwhelmed by unrelated tasks.
Similarly, the different departments and guilds within your kingdom should have well-defined interfaces, ensuring clear communication and efficient collaboration. A blacksmith shouldn’t be burdened with the duties of a baker, and a farmer shouldn’t be expected to navigate complex legal matters.
In this context, different professions should have their own specific interfaces, rather than a single, bloated interface that includes methods irrelevant to their work:
// Interfaces for Specific Skills
interface Metalworker {
void forge();
void repair();
}
interface FoodProducer {
void bake();
void cook();
}
interface Healer {
void treatWounds();
void diagnose();
}
// Profession Implementations
class Blacksmith implements Metalworker {
@Override
public void forge() { System.out.println("Blacksmith forges a sword."); }
@Override
public void repair() { System.out.println("Blacksmith repairs armor."); }
}
class Baker implements FoodProducer {
@Override
public void bake() { System.out.println("Baker bakes fresh bread."); }
@Override
public void cook() { System.out.println("Baker cooks a hearty stew."); }
}
class Herbalist implements Healer {
@Override
public void treatWounds() { System.out.println("Herbalist applies healing poultice."); }
@Override
public void diagnose() { System.out.println("Herbalist diagnoses an illness."); }
}
As long as each profession has it’s own interface, it’s easy to navigate your kingdom:
// Kingdom Class (Client)
class Kingdom {
public void assignTask(Metalworker worker) {
worker.forge();
}
public void assignTask(FoodProducer worker) {
worker.bake();
}
public void assignTask(Healer worker) {
worker.treatWounds();
}
}
// Usage
public class Main {
public static void main(String[] args) {
Kingdom kingdom = new Kingdom();
Blacksmith blacksmith = new Blacksmith();
Baker baker = new Baker();
Herbalist herbalist = new Herbalist();
kingdom.assignTask(blacksmith); // Blacksmith forges
kingdom.assignTask(baker); // Baker bakes
kingdom.assignTask(herbalist); // Herbalist treats
}
}
Now, each profession is responsible only for its own tasks, and the kingdom can assign work based on the worker’s specific skills. This promotes cleaner, more focused work.
Benefits
- Flexibility: you can easily introduce new professions or skills (who doesn’t want a bard in his kingdom?) without changing existing ones.
- Maintainability: code is easier to understand and modify because each interface has a clear purpose.
- Reduced Coupling: professions are only dependent on the interfaces they need, and don’t need to implement the unnecessary functionality.
D — The Dependency Inversion Principle (DIP): The Independent Merchant
No great kingdom is built without alliances in mind. And alliances mean trade! With trade managed properly, you will likely get a variety of new exotic valuables, and the people of your kingdom will be happier than ever!
But how to manage trade? A successful merchant doesn’t rely solely on a single source for goods; they establish relationships with various suppliers to ensure a steady flow of trade. This principle extends to all aspects of your kingdom. You, as a monarch of your kingdom, should avoid over-reliance on a single resource, ally, or trading partner. By diversifying your dependencies, you build resilience against unforeseen disruptions.
This means that your kingdom’s trading system shouldn’t be tightly coupled to specific merchants or goods. Instead, it should interact with them through abstract interfaces:
// Abstraction (Interfaces)
interface Merchant {
List<Goods> getAvailableGoods();
void sellGoods(List<Goods> goods);
}
interface Goods {
String getName();
int getPrice();
}
Any blacksmith, farmer, travelling merchant can be considered merchant:
// Low-level Implementations
class Blacksmith implements Merchant {
@Override
public List<Goods> getAvailableGoods() {
System.out.println("Here are all the blades I crafted!");
// Provide available goods
}
@Override
public void sellGoods(List<Goods> goods) {
System.out.println("This one will cost you 150 silver coins");
// Sell goods
}
}
class Farmer implements Merchant {
@Override
public List<Goods> getAvailableGoods() {
System.out.println("Here is all the bread I baked!");
// Provide available goods
}
@Override
public void sellGoods(List<Goods> goods) {
System.out.println("This one will cost you 5 silver coins");
// Sell goods
}
}
class TravellingMerchant implements Merchant {
@Override
public List<Goods> getAvailableGoods() {
System.out.println("Here are all the magical stones I have!");
// Provide available goods
}
@Override
public void sellGoods(List<Goods> goods) {
System.out.println("This one will cost you 50 gold coins");
// Sell goods
}
}
And of course, there should be some place for all the merchants to gether and sell their goods! All the purchases are made in the presence of the guards, so that there is no cheating in your kingdom! In our case, guards are ordered to guard specifically the Kingdom Market, so it’s the Kingdom Market’s responsibility to handle the transactions and provide with goods:
// High-level module
class KingdomMarket {
private List<Merchant> merchants;
public KingdomMarket(List<Merchant> merchants) {
this.merchants = merchants;
}
public void browseGoods() {
for (Merchant merchant : merchants) {
List<Goods> goods = merchant.getAvailableGoods();
// Display goods for each merchant
}
}
public void purchaseGoods(Merchant merchant, List<Goods> goods) {
merchant.sellGoods(goods);
// Handle transaction details
}
}
// Usage
public class Main {
public static void main(String[] args) {
List<Merchant> merchants = new ArrayList<>();
merchants.add(new Farmer());
merchants.add(new Blacksmith());
merchants.add(new TravellingMerchant());
KingdomMarket market = new KingdomMarket(merchants);
market.browseGoods(); // Display available goods from all merchants
// Example purchase:
List<Goods> itemsToBuy = new ArrayList<>(); // Choose specific goods from a merchant
market.purchaseGoods(merchants.get(0), itemsToBuy); // Buy from the first merchant
}
}
The Kingdom Market (the guards) does not need to know a merchant type to identify the fraud, which makes it easy for the guards to actually guard your Market.
Benefits
- Flexibility: you can easily add or change merchants without affecting the core market (and the guards!) logic.
- Reusability: the
KingdomMarket
class can be used with different types of merchants.
Conclusion: A Kingdom Built to Last
By adhering to the SOLID principles, you establish a kingdom that is not only strong and prosperous but also adaptable and enduring. Your castles will stand tall, your laws will remain just, and your people will thrive for generations to come. So, embrace these timeless principles and build a legacy that will be remembered throughout the ages.
May your kingdom be as strong as a dragon’s scales and your architecture as enduring as a dragon’s reign!