Pragmatic Programmer: Chapter 5

PhaseSmith·2022년 3월 26일
0

Life doesn’t stand still. Neither can the code that we write. In order to keep up with today’s near-frantic pace of change, we need to make every effort to write code that’s as loose—as flexible—as possible. Otherwise we may find our code quickly becoming outdated, or too brittle to fix, and may ultimately be left behind in the mad dash toward the future. (p. 227)

Topic 28. Decoupling

Coupling is the enemy of change, because it links together things that must change in parallel. This makes change more difficult: either you spend time tracking down all the parts that need changing, or you spend time wondering why things broke when you changed “just one thing” and not the other things to which it was coupled. (p. 228)

Couple only if it’s really necessary.

Tip 44. Decouple code is easier to change

Things to be cautious about:

  • Dependencies between unrelated modules or libraries
  • Changes to one module that proopagates through unrelated modules in the system or break stuff elsewhere in the system
  • Being afraid to change code because of the uncertainty that might occur
  • Meetings where everyone has to attend because no one is sure who will be affected by a change

Tip 45. Tell, Don’t Ask (TDA)

This principle says that you shouldn’t make decisions based on the internal state of an object and then update that object. (p. 231)

→ this practice will destroy the benefites of encapsulation

→ this will spread the knowledge of the implementation throughout the code

→ exposed to risk of coupling

Solution: TDA → a function should get the object of interest directly instead of searching from a list which includes other related objects

Note: TDA is not a law; it’s just a pattern to help us recognize problems of exposing certain aspects to the top-level API

Tip 46. Don’t Chain Method Calls

Try not to have more than one “.” when you access something.
And access something also covers cases where you use
intermediate variables. (p. 234)

Chains and Pipelines:

Passing from function to function → better \becausewe don’t rely on hidden implementation details

Pipelines do introduce some coupling, but this is far less a barrier to change the code.

Evils of Globalization:

Globally accessible data is an insidious source of coupling between application components. Each piece of global data acts as if every method in your application suddenly gained an additional parameter: after all, that global data is available inside every method. (p. 236)

Usually, the justification behind using global variables is the purpose of reusability. When you make code reusable, you should give it clean interfaces decoupling it from the rest of your code. This allows you to extract a method or module without dragging everything else along with it.

Tip 47. Avoid global data

Any mutable external resource is global data. If your application uses a database, datastore, file system, service API, and so on, it risks falling into the globalization trap. Again, the solution is to
make sure you always wrap these resources behind code that you control. (p. 237)

Tip 48. If it’s important enough to be global, wrap it in an API

Topic 29. Juggling the Real World

Theme: responsive applications

Events:

An event represents the availability of information. It might come from the outside world: a user clicking a button, or a stock quote update. It might be internal: the result of a calculation is ready, a search finishes. It can even be something as trivial as fetching the next element in a list.

Writing applications that respond to events will make them work better in the real world; their users will find them interactive, and the applications themselves will make better use of resources.

4 strategies for responsive applications:

  1. Finite state machines
  2. The Observer Pattern
  3. Publish/Subscribe
  4. Reactive Programming and Streams

Finite state machines (FSM):

State machines: Specification of how to handel events. Consists of a set of states, one of which is the current state.

For each state, we list the events that are significant to that state.

For each of these events, we define the new current state of the system.

Example:

The Observer Pattern

The Observables: a source of events

The Observers: a list of clients who are interested in those events

An observer registers its interest with the observable, typically by passing a reference to a function to be called. Subsequently, when the event occurs, the observable iterates down its list of observers and calls the function that each passed. The event is given as a paramter to call.

Drawbacks:

But the observer pattern has a problem: because each of the observers has to register with the observable, it introduces coupling. In addition, because in the typical implementation the callbacks are handled inline by the observable, synchronously, it can introduce performance bottlenecks. (p. 247)

Publish/Subscribe (pubsub):

Pubsub generalizes the observed pattern, at the same time, solving the problems of coupling and performance.

Publishers and Subscribers are connected via channels, which are each assigned a name.

Subscribers register interest in one or more of the named channels.

Publishers write events to these channels.

Unlike the observer pattern, the communication between the publisher and subscriber is handled outside your code, and is potentially asynchronous.

Most cloud service providers have pubsub offerings, allowing you to connect applications around the world. Every popular language will have at least one pubsub library.

Pubsub is a good technology for decoupling the handling of asynchronous events. It allows code to be added and replaced, potentially while the application is running, without altering existing code. (p. 249)

Drawbacks:

hard to see what is going on in a system that uses pubsub heavily → you can’t look at a publisher and immediately see which subscribers are involved with a particular message.

Compared to the observer pattern, pubsub is a great example of reducing coupling by abstracting up through a shared interface (the channel). However, it is still basically just a message passing system. (p.249)

Reactive Programming, Streams, and Events:

Under this paradigm, values react as the values they use change.

Streams: let us treat events as if they were a collection of data.

The current de facto baseline for reactive event handling is defined on the site http://reactivex.io, which defines a language-agnostic set of principles and documents some common implementations.

Event streams are normally populated as events occur, which implies that the observables that populate them can run in parallel.

Event streams unify synchronous and asynchronous processing behind a common, convenient API.

Topic 30. Transforming Programming

All programs transform data, converting an input into an output. And yet when we think about design, we rarely think about creating transformations. Instead we worry about classes and modules, data structures and algorithms, languages and frameworks.
We think that this focus on code often misses the point: we need to get back to thinking of programs as being something that transforms inputs into outputs. When we do, many of the details we previously worried about just evaporate. The structure becomes clearer, the error handling more consistent, and the coupling drops way down. (p.255)

Tip 49. Programming is about code, but programs are about data

Finding Transformations:

Top-down approach: start with the requirement and determine its inputs and outputs.

profile
우리는 데이터와 하나다

0개의 댓글