Programming Principles II: Going Beyond the Basics
Theodora Gyambrah
Passionate Software Engineer | Project Manager | Blockchain Developer | Engineering dreams - one line of code at a time
In our previous article, we delved into fundamental programming principles and practices, including KISS, YAGNI, DRY, Fail Fast, and Clean Code, all crucial for achieving efficient and maintainable code. If you haven't had a chance to read it, you can find it here .
In this article, we will further explore equally essential principles that will enable us to write code that not only functions effectively but also exhibits elegance, readability, and maintainability.
Now, let's delve into it:
1. Separation of Concerns(SoC)
SoC is a fundamental principle that promotes the idea that a software system should be divided into distinct, self-contained modules, each addressing a single concern or responsibility. The primary goal of SoC is to reduce complexity by isolating different aspects of functionality.
Here's an example of the SoC principle:
Suppose you're building a simple web application that allows users to create and manage tasks. In this scenario, the SoC principle can be applied in the following ways:
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<h1>Task Manager</h1>
<div>
<label for="task-description">Task Description:</label>
<input type="text" id="task-description" placeholder="Enter task description">
<button id="add-task">Add Task</button>
</div>
<div id="task-list">
<!-- Task list will be displayed here -->
</div>
<script src="app.js"></script>
</body>
</html>
/* styles.css */
body {
font-family: Arial, sans-serif;
}
/* Add more CSS styles as needed */
// app.js
import { addTaskToDataStore, getAllTasksFromDataStore } from './data-access';
// Function to add a task
const addTask = (description) => {
const taskDescription = document.getElementById("task-description").value;
if (taskDescription) {
const task = { description: taskDescription, completed: false };
// Use the data access function to add the task
addTaskToDataStore(task);
// Other implementation code
}
};
// Function to list tasks
const listTasks = () => {
const tasks = getAllTasksFromDataStore();
const taskListContainer = document.getElementById("task-list");
// Other implementation code
};
// Event handling for adding tasks
document.getElementById("add-task").addEventListener("click", addTask);
// data-access.js
// Simple in-memory data store for tasks
const taskData = [];
// Function to add a task to the data store
const addTaskToDataStore = (task) => {
taskData.push(task);
};
// Function to retrieve all tasks from the data store
const getAllTasksFromDataStore = () => {
return taskData;
};
// Export the functions to be used by the business logic
export { addTaskToDataStore, getAllTasksFromDataStore };
By following the SoC principle, we’ve separated the concerns in this example:
This separation makes the code more organized, maintainable, and extensible, as changes in one concern are less likely to impact other parts of the application.
2. Law of Demeter(LoD)
The Law of Demeter (LoD), also known as the Principle of Least Knowledge, is a fundamental design principle in object-oriented programming (OOP). Its primary objective is to enhance the modularity, maintainability, and overall design quality of software. To better understand the aims of LoD, let's delve into its key aspects:
In essence, LoD can be succinctly summarized as follows: "Every unit should only interact with the specific components or objects that it directly relies on, without delving deep into the internals of other units."
Let's consider this basic example:
Without LoD Principle:
class Person {
constructor(name) {
this.name = name;
}
getCompanyRevenue(company) {
// Violation of LoD
return company.finances.getRevenue();
}
}
class Company {
constructor() {
this.finances = new Finances();
}
}
class Finances {
getRevenue() {
return 1000000;
}
}
In this code, the Person class violates the Law of Demeter by directly accessing company.finances.getRevenue(). The Person class knows too much about the internal structure of the Company and its Finances.
领英推荐
With LoD Principle:
class Person {
constructor(name) {
this.name = name;
}
getCompanyRevenue(company) {
return company.calculateRevenue();
}
}
class Company {
constructor() {
this.finances = new Finances();
}
calculateRevenue() {
return this.finances.getRevenue();
}
}
class Finances {
getRevenue() {
return 1000000;
}
}
In this refactored code, the Person class interacts with the Company through a more limited interface, adhering more closely to the Law of Demeter. The Person class doesn't directly access the internal details of the Company, but instead, it interacts with the more abstract and encapsulated calculateRevenue method, reducing coupling and making the code more maintainable.
3. Tell, Don’t?Ask(TDA)
The Tell, Don’t Ask (TDA) principle emphasizes instructing objects to perform actions rather than querying their state and then making decisions based on that state. In other words, it encourages you to “tell” an object what you want it to do, rather than “asking” the object about its internal state and then deciding what action to take based on that information.
This principle aligns with the Law of Demeter principle as both emphasize the benefits of loose coupling and better encapsulation in software design.
Here’s a simple example to illustrate the TDA principle:
// Asking (violation of TDA)
if (account.getBalance() >= 100) {
account.withdraw(100);
}
// Telling (following TDA)
account.withdrawIfSufficientBalance(100);
In the first example, the code queries the account object for its balance and then decides to withdraw money based on that information, which can lead to external code making decisions for the account.?
In the second example, the code directly tells the account to withdraw money if it has a sufficient balance, encapsulating the logic within the account itself and following the TDA principle.
4. Law of Least Astonishment(LoLA)
The Law of Least Astonishment (LoLA), also referred to as the Principle of Least Astonishment (POLA), is a fundamental design principle that focuses on creating software and user interfaces that behave in a manner that is the least surprising or astonishing to both users and developers. The primary aim of LoLA is to minimize unexpected or counterintuitive behavior, resulting in more user-friendly and predictable software.
LoLA is particularly significant in User Interface (UI) design, as it plays a crucial role in ensuring that UI elements and interactions align with users' common expectations. Here's some examples of how LoLA applies to User Interface design:
Button Behavior:
Navigation Consistency:
Iconography:
In each of these examples, violating the Law of Least Astonishment would result in behaviors that are counterintuitive and surprising. Adhering to LoLA means designing software and user interfaces with users' expectations in mind, ensuring that interactions and behaviors align with what users commonly anticipate.
In Summary,
In this article, we've explored key programming principles: Separation of Concerns (SoC), Law of Demeter (LoD), Tell, Don't Ask (TDA), and Law of Least Astonishment (LoLA). These principles form the bedrock of clean, maintainable code, emphasizing modular, well-encapsulated design and user-friendly interfaces.
In the next article, we'll delve into the SOLID principles, a set of guidelines that take software design to the next level. Stay tuned as we uncover how SOLID principles enhance extensibility and adaptability in software architecture, ensuring your code remains robust and flexible over time.
Full-stack JavaScript Developer@Chipsoft. I’m ever ready to work
12 个月Nice and lovely article
--
1 年Senior dev