SWE : Structural Design Pattern

Part 3 of Design Pattern series

Pisit J.
Pisit J.
Mar 4 · 7 min read
image

1. Adapter

  • Create an intermediary abstraction that make two incompatible interfaces work together.
  • Use Case — Universal electrical adapter in our daily life.
  • Adapter lets you create a middle-layer class between your code to legacy class, 3rd-party class or any other class which you can’t change.
  • The complexity of the code increases because you need to create & maintain new interfaces and classes. If possible, it’s often simpler just to change the target class so that it matches the rest of your code.
Class with parameter x1, y1, x2, y2
Class with Incompatible parameter — x, y, width, height
Adapter
Main

2. Bridge

  • Decouples an abstraction from its implementation, so that the two can vary independently.
  • Use Case — A household switch controlling lights, electric fans, air conditioners, etc. is an example of the Bridge. The purpose of the switch is to turn a device on or off. The actual implementation making device on or off is internal mechanism of each device.
  • You can focus on high-level logic in the abstraction and low-level details in the implementation.
  • You can introduce new abstractions and new implementations independently from each other.
  • You might make the code more complicated by creating unnecessary abstraction if your new abstraction is quite similar to existing implementations.
Implementation
Abstraction by Bridge

Reference

3. Composite

  • Use the Composite when you have to implement a tree-like object structure. When you call a method, the objects themselves pass the request down the tree.
  • Use Case — Directories that contain files.
  • Composite can use with both simple and complex elements. You can treat them all the same via the common interface method — add & traverse.
  • It might be difficult to provide a common interface for classes whose functionality differs too much. In certain scenarios, you’d need to overgeneralize the component interface, making it harder to comprehend.
Component — traverse()
Composite — add() & traverse()
Main
Output

4. Decorator

  • Keyword — wrapper.
  • Create layers of business logic and compose objects with various combinations of this logic layer at runtime. The client code can treat all these objects in the same way, since they all follow a common interface.
  • Use the pattern when it’s not possible to extend an object’s behavior using inheritance — For class with final keyword, which prevents further extension of a class, the only way to reuse the existing behavior would be to wrap the class with your own wrapper, Decorator.
  • Use Case — Ice cream with custom topping. Ice cream like base object which is yummy on it’s own. Topping like decorator you add to make your ice cream more yummy.
  • You can add or remove responsibilities to a base object by recursively wrapping it at runtime without breaking the code.
  • You can divide a single class that implements many possible behaviors into several smaller classes.
  • You can combine several behaviors by wrapping an object into multiple decorators.
  • It’s hard to implement a decorator in such a way that its behavior doesn’t depend on the order in the decorators stack.
  • The initial configuration code of layers might look pretty hard to read when multiple decorators are applied.
Base
Decorator
Main
Output

5. Facade

  • Provide a unified interface of multiple subsystems.
  • A subsystem is more flexible and easier to reuse in general, but the amount of configuration and boilerplate grows ever larger — Facade helps by providing a shortcut to the most-used features of the subsystem which fit most client requirements.
  • Use Case — when customer is ordering goods from sales representative. This sales representative acts as Facade — providing an interface to the stock department, the order department, the billing department, and the shipping department.
  • Facade defines a higher-level interface that makes subsystems easier to use.
  • Facade often become a master interface that can do anything — God Object.

6. Flyweight

  • Keyword — cache.
  • Use caching & sharing to support usage of high-memory objects
  • When to use Flyweight — 1. the application needs to spawn a huge number of similar objects. — 2. the process often drains most available RAM if no optimization is applied — 3. the objects contain duplicate states which can be extracted & shared between multiple objects.
  • Use Case — Modern web browsers use this technique to prevent loading same images twice. Browser loads all new images of web page and places them the internal cache, Flyweight. The same images will be referenced to the cached one.
  • Flyweight is the most useful if you have limit resource of memory.
  • You might be trading RAM over CPU cycles because recalculation is required each time Flyweight method is called.

7. Private Class Data

  • Keyword — getter, setter
  • Control read/write access of class attributes
getter & setter —getName & setName
Main

8. Proxy

  • Act as placeholder to provide indirect access to an object with additional feature.
  • Proxy provides the same interface of base object, which makes them interchangeable for client.
  • Virtual Proxy — Lazy Initialization — is a placeholder for expensive-to-create objects. You can delay the object’s initialization to a time when it’s really needed. The real object is only created when a client first access the object.
  • Remote Proxy provides a local representative for an object that resides in a different address space — local execution of a service located on a remote server.
  • Protective Proxy controls access to a sensitive master object by checking permissions required prior to forwarding the request.
  • Logging Proxy can log each request before passing it to the service.
  • Caching Proxy can implement caching for recurring requests that always yield the same results. The proxy may use the parameters of requests as the cache keys.
  • A Reference Proxy — This is when you need to be able to dismiss heavyweight objects once there are no clients is using.
  • You can add new functionality to service object without changing service interface on client-side.
  • You can manage the lifecycle of the service object via Proxy.
  • Proxy works even if the service isn’t ready or is not available.
  • The code may become more complicated since you need to introduce new classes.
Interface shared between Proxy and Base Class
Base Class
Proxy
Main
Output