LightMySky
به انگلیسی

Design patterns: naming a solution that keeps recurring

Design patterns describe recurring problems and arrangements of collaborating parts. Strategy uses replaceable rules; Observer notifies registered listeners. Their shared contracts help keep changes behind a boundary. Choosing a pattern means comparing its benefit with the extra structure it adds.

No account needed. Progress saves in this browser.

What a learner can do afterwards

  • Recognise the strategy arrangement in code that does not use the word
  • Apply the observer arrangement to decouple a producer from its listeners
  • Give an example where a pattern adds indirection without buying anything

1 · Read

You have used interfaces to describe what callers may rely on. A design pattern describes a recurring problem, an arrangement that addresses it and the conditions where it helps. Its name gives a team shared vocabulary for the roles and tradeoffs. Recognising that arrangement does not prove its implementation meets the requirements; those still need checking.

Try it together

Strategy delegates behaviour to a replaceable rule with a shared contract. In Python, rule = standard_rule selects an object; rule.fee(amount) calls it. After rule = reduced_rule, the next rule.fee(amount) uses the replacement. Both rules must keep the same inputs and promised behaviour. They need not inherit from one parent. For a few fixed cases, a short conditional may be easier.

rule = standard_rulerule.fee(amount)rule = reduced_rulerule.fee(amount)
Read in order. The same call uses the rule currently selected. Both rules must keep its contract.

Singleton restricts construction to at most one instance of a class within a stated scope, such as one running process, and provides access to that shared instance. It concerns objects, not how often the class is declared. A process-local Singleton does not automatically share its object with another process. Needing one object does not by itself make Singleton the best design.

Observer lets listeners register with a producer. Each listener provides update(value). When a sensor's value changes, it calls that method on each registered listener. A new display can register without adding display-specific code to the sensor. The sensor knows listener references and the notification contract, but need not know their concrete classes. The contract must specify any timing or failure-handling requirements.

sensor value changeslistener.update(value)listener handles value
This trace follows one registered listener. Other registered listeners receive their own calls.

Object composition builds behaviour by holding and using other objects. It is a technique used by several named patterns, not one fixed class arrangement. An account can delegate sending to a sender and storage to a store. If retry logic changes inside the sender while the whole send(message) contract stays unchanged, the account can keep its call. Changing the contract may require caller changes.

Naming a repeated setting is a reuse technique. In Python, LIMIT = 14 followed by saved = LIMIT gives saved the integer 14. Later, LIMIT = 22 leaves saved at 14. To refresh saved, run saved = LIMIT after that change; it then holds 22. A copy made before the change keeps 14. Checks that need the current setting can read LIMIT directly. Reassigning a name does not update earlier copies or rerun calculations.

Compare a pattern's benefit with its extra calls, objects and maintenance. A replaceable rule can help when behaviour varies or needs separate testing. If one direct function meets the requirements and no replacement or separate testing is needed, swapping layers have no stated benefit. Other patterns solve other problems, so judge the particular arrangement against the actual requirements.

Pattern names describe roles and tradeoffs. Keep shared contracts, state a Singleton's scope, and check how each extra layer helps meet the requirements.

Lesson, questions and flashcards written for this dataset. Textbook and academy records were consulted for concepts and question design; their wording is not reproduced.

These five readings were written for this dataset with new explanations and examples. Textbook and academy records informed the subject coverage; no source passages or figures are reproduced.

More to read

Record the reason for a pattern

Before choosing a pattern, write the requirement it will help you meet. Identify what is expected to vary and what callers should be able to keep unchanged.

For Strategy, record the operation that every rule supplies. List its inputs and the behaviour callers may rely on. For Observer, record how a listener registers and what notification it receives.

Then compare the proposal with a direct alternative. A conditional may be easier to follow when there are only a few fixed cases. Separate rule objects may help when rules vary or need independent tests. Neither choice wins from its name alone.

Record costs as well as benefits. Another object can mean another call to follow while investigating a problem. A listener list needs rules for registration and removal.

A useful design note connects each added part to a requirement. That connection lets a reviewer question the design without treating a familiar pattern name as proof.

Trace a rule without its pattern name

A preview tool has two formatter objects. Each provides format(record), accepting the same kind of record and returning display text.

The tool first selects a formatter:

formatter = brief_formatter

It requests a result through that object:

text = formatter.format(record)

Later, it selects another formatter:

formatter = detailed_formatter

The next formatter.format(record) call uses detailed_formatter. The expression making the request has not changed. The selected collaborator supplies the behaviour. These roles form Strategy even though none of the names says Strategy.

The objects do not have to inherit from one parent class. They do have to keep the operation's contract. Merely having a method called format is not enough if it accepts incompatible inputs or returns an unusable result.

Tracing identifies the arrangement. Deciding whether to keep it requires a second question: what requirement benefits from this separation?

Specify what a notification promises

A measurement recorder notifies several displays when a reading arrives. Each display registers as a listener and provides update(value).

That describes the Observer arrangement, but leaves delivery decisions open. Does the recorder call listeners one at a time? If one raises an error, are the others still called? Can a listener stop receiving updates? A pattern name does not answer these questions for the program.

The team must put its required behaviour into the notification contract. A listener replacement must obey that contract, not just use the expected method name.

Separate the producer's responsibility from each display's work. The producer manages registration and sends notifications. A display decides how to present the value it receives. Adding a display should not require the producer to inspect the display's concrete class.

Check these boundaries against the requirements. Observer explains a communication arrangement; it does not by itself establish timing or failure-handling behaviour.

Decide when a result should change

A stored result records the calculation made when its assignment ran. Consider these Python statements in order:

price = 8

quantity = 5

subtotal = price * quantity

quantity = 6

The multiplication produces 40. Assigning 6 to quantity afterwards does not rerun that multiplication, so subtotal remains 40.

That value is useful if the program wants a snapshot of the earlier order. If it wants the current subtotal, it must calculate price * quantity again. With the current values, that calculation produces 48.

Giving a calculation a function name can avoid repeating its expression while allowing fresh inputs on each call. Storing one result has a different effect: later uses share the earlier result until it is replaced.

Before combining repeated expressions into one stored value, decide whether all uses need the same snapshot. Reducing duplicated code and keeping a result current are separate requirements.

Separate responsibility from ownership

Object composition builds behaviour using other objects. Holding a reference to a part does not, by itself, say who created the part or whether it is shared.

A receipt service might receive a printer object from its caller. It requests printing through print_receipt(receipt). Several services could use the same printer, so this relationship does not establish exclusive ownership.

Another service might create its own private archive object. That is a different choice about creation and sharing. Decide those rules separately from deciding which responsibility belongs to the part.

A separate printer can isolate changes to printing while its public contract stays usable. If a replacement printer requires extra information from callers, the receipt service may need changes too.

A fixed responsibility can still deserve a separate object for clarity or testing. Runtime replacement is one possible benefit of composition, not its definition. Choose the parts and their contracts to fit the work the program must do.

2 · Watch

به انگلیسی

Take it off screen

Print a worksheetA4 with an answer key page for grown-ups. No screen, no internet.

Where it sits

Then practise

8 questions wait behind this lesson, each with its answer explained. Every answer feeds the sky: stars light as they are learned, and dim when it is time to come back.

Spotted a problem on this page? Tell us
Design patterns: naming a solution that keeps recurring · Computing, ages 19 to 21 · LightMySky