Structural patterns explain how to assemble objects and classes into larger structures while keeping these structures flexible and efficient.
graph TD
A[Structural Patterns] --> B[Adapter]
A --> C[Bridge]
A --> D[Composite]
A --> E[Decorator]
A --> F[Facade]
A --> G[Flyweight]
A --> H[Proxy]
B --> I[Adapts interfaces]
C --> J[Separates abstraction from implementation]
D --> K[Creates tree structures]
E --> L[Adds responsibilities dynamically]
F --> M[Simplifies complex systems]
G --> N[Shares common state]
H --> O[Controls access]
| Pattern | Purpose | Complexity | Performance Impact | Common Use Cases |
|---|---|---|---|---|
| Adapter | Interface conversion | Low | Minimal | Legacy system integration |
| Bridge | Implementation separation | Medium | Low | Platform independence |
| Composite | Hierarchical structures | Medium | Low | UI components, file systems |
| Decorator | Dynamic enhancement | Low | Low | I/O streams, UI features |
| Facade | Simplification | Low | None | API wrappers |
| Flyweight | Resource sharing | High | Positive | Graphics, game objects |
| Proxy | Access control | Medium | Varies | Resource management |
// Target interface
public interface MediaPlayer {
void play(String audioType, String fileName);
}
// Adapter
public class MediaAdapter implements MediaPlayer {
private AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter(String audioType) {
if(audioType.equalsIgnoreCase("vlc")) {
advancedMusicPlayer = new VlcPlayer();
}
}
@Override
public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("vlc")) {
advancedMusicPlayer.playVlc(fileName);
}
}
}
// Abstraction
public abstract class Shape {
protected DrawAPI drawAPI;
protected Shape(DrawAPI drawAPI) {
this.drawAPI = drawAPI;
}
public abstract void draw();
}
// Implementation
public interface DrawAPI {
void drawCircle(int x, int y, int radius);
}
// Concrete Implementation
public class RedCircle implements DrawAPI {
@Override
public void drawCircle(int x, int y, int radius) {
System.out.println("Drawing Circle[ color: red, radius: " + radius + "]");
}
}
// Component
public abstract class FileSystemNode {
protected String name;
public abstract void ls();
public abstract long getSize();
}
// Leaf
public class File extends FileSystemNode {
private long size;
@Override
public void ls() {
System.out.println(name);
}
@Override
public long getSize() {
return size;
}
}
// Composite
public class Directory extends FileSystemNode {
private List<FileSystemNode> children = new ArrayList<>();
public void add(FileSystemNode node) {
children.add(node);
}
@Override
public void ls() {
System.out.println(name);
for(FileSystemNode node : children) {
node.ls();
}
}
@Override
public long getSize() {
return children.stream()
.mapToLong(FileSystemNode::getSize)
.sum();
}
}
// Component
public interface Coffee {
double getCost();
String getDescription();
}
// Concrete Component
public class SimpleCoffee implements Coffee {
@Override
public double getCost() {
return 1;
}
@Override
public String getDescription() {
return "Simple coffee";
}
}
// Decorator
public abstract class CoffeeDecorator implements Coffee {
protected final Coffee decoratedCoffee;
public CoffeeDecorator(Coffee coffee) {
this.decoratedCoffee = coffee;
}
@Override
public double getCost() {
return decoratedCoffee.getCost();
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription();
}
}
// Concrete Decorator
public class Milk extends CoffeeDecorator {
public Milk(Coffee coffee) {
super(coffee);
}
@Override
public double getCost() {
return super.getCost() + 0.5;
}
@Override
public String getDescription() {
return super.getDescription() + ", milk";
}
}
// Subsystem classes
public class CPU {
public void freeze() { }
public void jump(long position) { }
public void execute() { }
}
public class Memory {
public void load(long position, byte[] data) { }
}
// Facade
public class ComputerFacade {
private CPU processor;
private Memory ram;
public ComputerFacade() {
this.processor = new CPU();
this.ram = new Memory();
}
public void start() {
processor.freeze();
ram.load(BOOT_ADDRESS, BOOT_DATA);
processor.jump(BOOT_ADDRESS);
processor.execute();
}
}
// Flyweight
public class Character {
private char symbol;
private String color;
private Font font;
// Intrinsic state
public Character(char symbol) {
this.symbol = symbol;
}
// Extrinsic state passed in
public void draw(String color, Font font) {
this.color = color;
this.font = font;
// Draw the character
}
}
// Flyweight Factory
public class CharacterFactory {
private Map<Character, Character> characters = new HashMap<>();
public Character getCharacter(char symbol) {
Character character = characters.get(symbol);
if(character == null) {
character = new Character(symbol);
characters.put(symbol, character);
}
return character;
}
}
// Subject
public interface Image {
void display();
}
// Real Subject
public class RealImage implements Image {
private String fileName;
public RealImage(String fileName) {
this.fileName = fileName;
loadFromDisk();
}
private void loadFromDisk() {
System.out.println("Loading " + fileName);
}
@Override
public void display() {
System.out.println("Displaying " + fileName);
}
}
// Proxy
public class ProxyImage implements Image {
private RealImage realImage;
private String fileName;
public ProxyImage(String fileName) {
this.fileName = fileName;
}
@Override
public void display() {
if(realImage == null) {
realImage = new RealImage(fileName);
}
realImage.display();
}
}