Programming Principles II: Going Beyond the Basics

Programming Principles II: Going Beyond the Basics

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:

  • User Interface (UI) Concern: The user interface is responsible for rendering and interacting with the user. We'll create an HTML file for the UI and use CSS for styling.

<!-- 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 */
        


  • Business Logic Concern: The business logic is responsible for handling task management functionality. We'll create a separate JavaScript file for this.

// 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 Concern: Data access typically involves communication with a database or storage system to manage data storage and retrieval for our task management application. We'll create another JavaScript file for this.

// 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:

  • The UI is defined in index.html and styles.css files.
  • The business logic for task management is in the app.js file.
  • Data access such as saving tasks to the database is handled in data-access.js file.

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:

  • Promoting Modularity: LoD encourages a design approach that divides software into smaller, more manageable components or modules. These modules are defined by their distinct responsibilities, enhancing organization and maintainability.
  • Reducing Coupling: In software engineering, "coupling" indicates the level of interdependence between different components or objects. LoD advocates for loose coupling, which means that components or objects should minimize their knowledge of the internal workings of others. This promotes independence and flexibility within the software architecture.
  • Promoting Encapsulation: Encapsulation emphasizes the need for objects to hide their internal details while offering well-defined, consistent interfaces for interactions. LoD reinforces this concept by discouraging direct access to an object's internal state. Instead, it encourages interactions with objects through their publicly exposed methods and properties.
  • Enhancing Maintainability: By following the principles of modularity, reduced coupling and strong encapsulation, you create a codebase that is more manageable and adaptable over time. When components are loosely coupled and well encapsulated, you gain the ability to modify or enhance one part of the system without causing unintended issues in other parts.

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:

  • Expected Behavior: In a well-designed user interface, buttons should visually appear as clickable elements, and when clicked, they should perform an action or trigger an event.
  • Violating LoLA: If a button doesn't appear clickable or fails to respond when clicked, it would astonish users and deviate from their expected interaction with UI elements.

Navigation Consistency:

  • Expected Behavior: Navigation menus or elements should follow established conventions. For instance, users anticipate that clicking a website's logo typically returns them to the homepage, and clicking a navigation menu item leads to the corresponding section or page.
  • Violating LoLA: If a website's logo unexpectedly takes users to an unrelated page or the navigation menu items behave inconsistently, it would surprise users and make navigation less intuitive.

Iconography:

  • Expected Behavior: Icons should employ widely recognized symbols and representations. For example, a trash can icon usually signifies a delete action, and a floppy disk icon implies saving.
  • Violating LoLA: Using non-standard or unconventional icons can bewilder users, as they might not easily understand the intended actions associated with the icons.

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.


Godbless Yankson

Full-stack JavaScript Developer@Chipsoft. I’m ever ready to work

12 个月

Nice and lovely article

回复

要查看或添加评论,请登录

社区洞察

其他会员也浏览了