From c6e6df028ad32464c6ef63fb486660d903134d5e Mon Sep 17 00:00:00 2001 From: chenyo-17 Date: Tue, 5 Nov 2024 21:28:51 +0100 Subject: [PATCH] post: continued philosophy --- ...tes:-a-philosophy-of-software-design-.html | 200 +++++++++++++---- index.html | 200 +++++++++++++---- ...otes:-a-philosophy-of-software-design-.org | 61 +++++- rss.xml | 202 ++++++++++++++---- static/pass-through-methods.png | Bin 0 -> 53957 bytes tag-book.html | 200 +++++++++++++---- tag-design.html | 200 +++++++++++++---- tag-software.html | 200 +++++++++++++---- 8 files changed, 1008 insertions(+), 255 deletions(-) create mode 100644 static/pass-through-methods.png diff --git a/2024-10-23-book-notes:-a-philosophy-of-software-design-.html b/2024-10-23-book-notes:-a-philosophy-of-software-design-.html index 43c76b7..6710dff 100644 --- a/2024-10-23-book-notes:-a-philosophy-of-software-design-.html +++ b/2024-10-23-book-notes:-a-philosophy-of-software-design-.html @@ -41,39 +41,51 @@

Chenyo's org-static-blog

Table of Contents

-This is a personal note for the book “A Philosophy of Software Design” (2018) by John Ousterhout. +This is a personal note for the book “A Philosophy of Software Design” (2018, John Ousterhout).

-
-

1. Introduction

+
+

1. Introduction

  • The most fundamental problem in CS is problem decomposition: how to divide a complex problem into pieces that can be solved independently.
  • @@ -94,16 +106,16 @@

    1. Introduction

-
-

2. Complexity

+
+

2. Complexity

  • Complexity is incremental.
  • Developers must adopt a zero tolerance philosophy.
-
-

2.1. Definition

+
+

2.1. Definition

  • Complexity is anything related to the structure of a software system that makes it hard to understand and modify the system.
  • @@ -115,8 +127,8 @@

    2.1. Definition

-
-

2.2. Symptoms

+
+

2.2. Symptoms

  • Change amplification: a seemingly simple change requires code modifications in many different places.
  • @@ -130,8 +142,8 @@

    2.2. Symptoms

-
-

2.3. Causes

+
+

2.3. Causes

  • Complexity is caused by two things: dependencies and obscurity.
  • @@ -151,8 +163,8 @@

    2.3. Causes

-
-

3. Programming mindsets

+
+

3. Programming mindsets

  • Tactical programming: the main focus is to get something working, e.g., a new feature or a bug fix.
  • @@ -160,8 +172,8 @@

    3. Programming mindset
  • Once a code base turns to spaghetti, it is nearly impossible to fix.
-
-

3.1. Strategic programming

+
+

3.1. Strategic programming

  • Requires an investment mindset to improve the system design rather than taking the fastest path to finish the current task.
  • @@ -171,7 +183,7 @@

    3.1. Strategic program

-
+
strategic-tactical.jpeg
Figure 1: Strategic vs Tactical programming (Source)
@@ -179,8 +191,8 @@

3.1. Strategic program

-
-

4. Modular design

+
+

4. Modular design

  • The goal of modular design is to minimize the dependencies between modules.
  • @@ -200,8 +212,8 @@

    4. Modular design

-
-

4.1. Interface

+
+

4.1. Interface

  • A module interface contains two information: formal and informal.
  • @@ -214,8 +226,8 @@

    4.1. Interface

-
-

4.2. Abstraction

+
+

4.2. Abstraction

  • An abstraction is a simplified view of an entity, which omits unimportant details. @@ -224,7 +236,7 @@

    4.2. Abstraction

  • A detail can only be omitted if it is unimportant, otherwise obscurity occurs.
  • In modular programming, each module provides an abstraction in form of its interface.
  • -
  • The key to designing abstractions is to understand what is important and to look for designs that minimize the amount of important information. +
  • The key to designing abstractions is to understand what is important.
    • E.g., how to choose storage blocks for a file is unimportant to users, but the rules for flushing data to secondary storage is important for databases, hence it must be visible in the file system interface.
    • Garbage collectors in Go and Java do not have interface at all.
    • @@ -233,8 +245,8 @@

      4.2. Abstraction

  • -
    -

    5. Information hiding

    +
    +

    5. Information hiding

    • Information hiding is the most important technique for achieving deep modules.
    • @@ -247,8 +259,8 @@

      5. Information hiding<

    -
    -

    5.1. Information leakage

    +
    +

    5.1. Information leakage

    • The leakage occurs when a design decision is reflected in multiple modules. thus creating dependencies between the modules. @@ -271,8 +283,8 @@

      5.1. Information leaka

    -
    -

    5.2. Temporal decomposition

    +
    +

    5.2. Temporal decomposition

    • Temporal decomposition is a common cause of information leakage.
    • @@ -287,6 +299,110 @@

      5.2. Temporal decompos

    +
    +

    6. General-Purpose modules

    +
    +
      +
    • General-purpose modules can be used to address a broad range of problems such that it may find unanticipated uses in the future (cf. investment mindset).
    • +
    • Special-purpose modules are specialized for today’s needs, and can be refactored to make it general-purpose when additional uses are required (cf. incremental software development).
    • +
    • The author recommends a “somewhat general-purpose” fashion: the functionality should reflect the current needs, but the interface should be general enough to support multiple uses.
    • +
    • The following questions can be asked to find the balance between general-purpose and special-purpose approach: +
        +
      • What is the simplest interface that will cover all current needs?
      • +
      • How many situations will a method be used?
      • +
      • Is the API easy to use for the current needs (not go too far)?
      • +
    • +
    +
    +
    +

    6.1. Example: GUI text editor design

    +
    +
      +
    • Specialized design: use individual method in the text class to support each high-level features, e.g., backspace(Cursor cursor) deletes the character before the cursor; delete(Cursor cursor) deletes the character after the cursor; deleteSelection(Selection selection) deletes the selected section.
    • +
    • The specialized design creates a high cognitive load for the UI developers: the implementation ends up with a large number of shallow methods so a UI developer had to learn all of them. +
        +
      • E.g., backspace provides a false abstraction as it does not hide the information about which character to delete.
      • +
    • +
    • The specialized design also creates information leakage: abstractions related to the UI such as backspace key and selection, are reflected in the text class, increasing the cognitive load for the text class developers.
    • +
    • General-purpose design define API only in terms of basic text features without reflecting the higher-level operations. +
        +
      • Only three methods are needed to modify a text: insert(Position position, String newText), delete(Position start, Position end) and changePosition(Position position, int numChars). +
          +
        • The new API uses a more generic Position to replace a specific user interface Cursor.
        • +
        • The delete key can be implemented as text.delete(cursor, text.ChangePosition(cursor, 1)), the backspace key can be implemented as text.delete(cursor, text.ChangePosition(cursor, -1)).
        • +
      • +
    • +
    • The new design is more obvious, e.g., the UI developer knows which character to delete from the interface, and also has less code overall.
    • +
    • The general-purpose methods can also be used for new feature, e.g., search and replace text.
    • +
    +
    +
    +
    +
    +

    7. Layers of abstractions

    +
    +
      +
    • Software systems are composed into layers, where higher layers use the facilities provided by lower layers; each layer provides an abstraction different from the layers above or below it. +
        +
      • E.g., a file in the uppermost layer is an array of bytes and is a memory cache of fixed-size disk blocks in the next lower layer.
      • +
    • +
    • A system contains adjacent layers with similar abstractions is a red flag of class decomposition problem.
    • +
    • The internal representations should be different from the abstractions that appear in the interface; if the interface and the implementation have similar abstractions, the class is shallow.
    • +
    +
    +
    +

    7.1. Pass-through methods

    +
    +
      +
    • A pass-through method is a method that does little except invoke another method, whose signature is similar or identical to the callee function.
    • +
    • Pass-through methods usually indicates there is not a clean division of responsibility between classes.
    • +
    • Pass-through methods also create dependencies between classes.
    • +
    • The solution is to refactor the classes, e.g., expose the lower level class directly to the higher level (b), redistribute the functionality (c) or merge them (d):
    • +
    + + +
    +pass-through-methods.png + +
    Figure 2: Refactor pass-through methods
    +
    +
    +
    +
    +

    7.2. Pass-through variables

    +
    +
      +
    • A pass-through variable is a variable that is passed down through a long chain of methods.
    • +
    • Pass-through variables add complexity as all intermediate methods must be aware of the existence and need to be modified when a new variable is used.
    • +
    • The author’s solution is to introduce a context object which stores all application’s global states, e.g., configuration options and timeout value, and there is one context object per system instance.
    • +
    • To avoid passing through the context variable, a reference to the context can be saved in other objects. +
        +
      • When a new object is created, the context reference is passed to the constructor.
      • +
    • +
    • Contexts should be immutable to avoid thread-safety issues and may create non-obvious dependencies.
    • +
    +
    +
    +
    +

    7.3. Acceptable interface duplication

    +
    +
      +
    • Dispatcher: a method that uses its arguments to select a specific method to invoke and passes most of its arguments. +
        +
      • E.g., when a web server receives an HTTP request, it invokes a dispatcher to examine the URL and selects a specific method to handle the request.
      • +
    • +
    • Polymorphous methods, e.g., len(string) and len(array) reduces cognitive load; they are usally in the same layer and do not invoke each other.
    • +
    • Decorator: a wrapper that takes an existing object and extends its functionality.
    • +
    • Decorators are often shallow and contain pass-through methods, one should consider following alternatives before using them: +
        +
      • Add the new functionality directly to the class if it is relatively general-purpose; or merge it with the specific use case if it is specialized.
      • +
      • Merge the new functionality with an existing decorator to make the existing decorator deeper.
      • +
      • Implement it as a stand-alone class independent of the base class, e.g., Window and ScrollableWindow.
      • +
    • +
    +
    +
    +