Code Companion
Java

Programming technique · B3.2.5 · Higher level only

Singleton, Factory and Observer

A design pattern is a reusable way of organising classes and objects around a problem that appears repeatedly. The pattern is the design idea, not one exact block of code to memorise.

IB DP CS standard B3.2.5: Explain common design patterns—Singleton, Factory and Observer—and how they can be applied to recurring programming challenges.

Start with the recurring problem

Do not begin by asking “which pattern can I force into this program?” Start with the design pressure. Patterns are useful names for solutions that have already proved useful in similar situations.

1

One shared instance

Many parts of the program need the same object and creating independent copies would be wrong.

Singleton
2

Centralised creation

The program must choose among concrete object types without spreading construction decisions through callers.

Factory
3

One-to-many updates

When one object changes or publishes an event, several interested objects should be notified.

Observer
A pattern earns its place by solving a recurring design problem. Extra classes and indirection are costs, so “using a pattern” is not automatically better design.

Singleton: one shared object

Suppose the whole application should use one AppSettings object. If every screen creates its own settings object, changing the theme on one copy does not change the others. Singleton provides one shared instance and a controlled way to obtain it.

The classic Java form combines a private constructor, a class-owned instance field and a static access method. Callers cannot use new AppSettings() directly.

Python can enforce one class instance through __new__, although Python programs may also use a module-level shared object when that simpler design is sufficient. The syllabus idea is the single shared instance and controlled access, not one compulsory syntax recipe.

Why it can help

One identity avoids conflicting copies of genuinely shared state and gives the program one known access point.

Why to be cautious

Singleton can behave like global state: dependencies become less visible, tests can interfere with shared state, and many classes may become tightly coupled to the singleton.

Factory: put the creation decision in one place

Suppose a caller needs a badge but the concrete class depends on a choice such as achievement or participation. Without a factory, many callers may repeat the same construction if/else logic.

In Java, the factory can return the shared parent or abstract type. The caller works with Badge while the factory decides which concrete subclass to instantiate.

Python does not need a declared parent type for this small example. The useful idea is still the same: the caller asks the factory for an object and then relies on the common operation rather than constructing each concrete class itself.

Why it can help

Concrete construction is localised. Adding or changing a creation rule can require fewer changes to the rest of the program.

Why to be cautious

A factory adds another layer. If creation is trivial and never varies, the extra class or method may make a small program harder rather than easier to follow.

Observer: publish once, notify many

A school notice board should not contain separate hard-coded calls for every possible display. Instead, interested objects subscribe. When a notice is published, the subject sends the same update to every current observer.

The important structure is a one-to-many dependency: observers register with the subject, and the subject notifies the registered objects through a common update operation. Adding another observer should not require a new type-specific branch in the publisher.

Why it can help

The publisher is less coupled to individual receivers. New observer types can join the same notification mechanism without rewriting the publishing algorithm.

Why to be cautious

Updates become indirect. With many observers it can be harder to see what an event will trigger, in what order, and whether an observer should be removed.

These patterns reuse ideas from the rest of OOP

PatternEarlier idea it builds onWhat changes
SingletonClass-owned/static state and encapsulationObject creation is controlled so the design exposes one shared instance.
FactoryAbstraction and polymorphismConstruction of a concrete subtype is moved behind one creation responsibility.
ObserverAssociations plus common polymorphic behaviourA publisher keeps references to interested objects and sends each the same update operation.
Pattern names describe roles, not new inheritance rules. A class may participate in a pattern using inheritance, composition, aggregation or plain association depending on the design. Do not infer a UML diamond merely because a class is called an Observer, Factory or Singleton.

Compare before choosing

QuestionSingletonFactoryObserver
Recurring problemToo many instances of one shared objectCreation logic spread across callersPublisher tightly coupled to many receivers
Core moveControl access to one instanceCentralise object creationRegister observers and notify them
Main benefitConsistent shared identity/stateLower coupling to concrete classesFlexible one-to-many updates
Typical costHidden/global-style dependenciesExtra indirectionLess obvious event flow

Choose the pattern from the problem

Name the recurring design problem first, then justify the pattern.

  1. A program needs exactly one shared configuration object that every screen should read from. Which pattern is the best starting point?

    Reveal model answer

    Singleton. Its intent is to provide one shared instance with a controlled access point. You should still justify why global shared state is appropriate rather than using it automatically.

  2. A program must create different exporter objects from a file-format choice, but the caller should not know each concrete exporter class. Which pattern?

    Reveal model answer

    Factory. It centralises the creation decision and returns an object through a common role or common set of operations.

  3. One sensor changes state and several displays should be updated automatically. Which pattern?

    Reveal model answer

    Observer. The subject/publisher keeps track of observers and notifies them when the relevant event occurs.

  4. Does putting every new expression inside a class called SomethingFactory automatically make the design a useful Factory pattern?

    Reveal model answer

    No. The useful design idea is centralising a recurring object-creation decision so callers depend less on concrete classes. A wrapper with no real creation responsibility only adds indirection.

  5. Why can Observer become harder to debug than a direct method call?

    Reveal model answer

    One event may trigger several indirect updates. The control flow, notification order and current subscription list are less obvious than one direct caller-to-receiver call.

How much implementation do you need?

B3.2.5 uses the command term Explain. You need to recognise Singleton, Factory and Observer, explain the problem each addresses, trace a small example, justify where the pattern is useful and discuss a relevant trade-off. Small completion or extension tasks are useful evidence, but this standard does not require memorising industrial-strength framework implementations.

Challenges Choose one

Choose a challenge that feels appropriate for you. Code heat is only a rough estimate, not a fixed level.

Repair the Shared Audit Log

Challenge ID: PC-T26-C01 · Standards: B3.2.5

Repair the supplied AuditLog design so callers use one shared instance instead of constructing multiple logs. Demonstrate object identity, then add concise comments explaining why Singleton fits this scenario and one disadvantage of using shared global-style state. Do not claim Singleton is automatically the best design whenever a class should be convenient to access.

Scaffold available

Extend the Receipt Factory

Challenge ID: PC-T26-C02 · Standards: B3.2.5

Complete the supplied ReceiptFactory and add a PrintReceipt concrete type. The caller must request a receipt through the factory and use only the common Receipt role; it must not contain an if/else chain that constructs concrete receipt subclasses. Test at least two receipt types, then explain how the pattern localises a recurring creation decision and one cost of the extra indirection.

Scaffold available

Match Feed Observers

Challenge ID: PC-T26-C03 · Standards: B3.2.5

Complete MatchFeed so listener objects can subscribe and every published match event is sent to all current listeners through the same update operation. Add a CoachDisplay listener and prove that MatchFeed does not need a CoachDisplay-specific branch. Explain why this is Observer, and give one maintainability benefit and one debugging/control-flow cost.

Scaffold available

B3.2 multiple-class OOP complete

You have now connected inheritance, polymorphism, abstraction, composition/aggregation and three common design patterns. The important skill is not collecting keywords: it is choosing and explaining a class relationship or structure that matches the problem.