Object Assembling
Adapter
The Adapter pattern is a structural design pattern that allows objects with incompatible interfaces to work together.
// Target interface
interface Target {
void request();
}
// Adaptee (the class with an incompatible interface)
class Adaptee {
void specificRequest() {
System.out.println("Adaptee's specific request");
}
}
// Adapter class that implements the Target interface and delegates requests to the Adaptee
class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
// Client code
public class Main {
public static void main(String[] args) {
// Create an Adaptee object
Adaptee adaptee = new Adaptee();
// Create an Adapter object and pass the Adaptee to it
Target adapter = new Adapter(adaptee);
// Call the request method of the Target interface
adapter.request();
}
}
Bridge
The Bridge pattern is a structural design pattern that decouples an abstraction from its implementation, allowing both to vary independently.
// Abstraction
abstract class Shape {
protected Color color;
public Shape(Color color) {
this.color = color;
}
abstract void draw();
}
// Implementor
interface Color {
void applyColor();
}
// Concrete Implementor
class RedColor implements Color {
@Override
public void applyColor() {
System.out.println("Applying red color");
}
}
class GreenColor implements Color {
@Override
public void applyColor() {
System.out.println("Applying green color");
}
}
// Refined Abstraction
class Circle extends Shape {
public Circle(Color color) {
super(color);
}
@Override
void draw() {
System.out.print("Drawing Circle - ");
color.applyColor();
}
}
class Square extends Shape {
public Square(Color color) {
super(color);
}
@Override
void draw() {
System.out.print("Drawing Square - ");
color.applyColor();
}
}
// Client code
public class Main {
public static void main(String[] args) {
Shape redCircle = new Circle(new RedColor());
Shape greenSquare = new Square(new GreenColor());
redCircle.draw(); // Drawing Circle - Applying red color
greenSquare.draw(); // Drawing Square - Applying green color
}
}
Composite
The Composite pattern is a structural design pattern that allows you to compose objects into tree structures to represent part-whole hierarchies.
import java.util.ArrayList;
import java.util.List;
// Component
interface Component {
void operation();
}
// Leaf
class Leaf implements Component {
private String name;
public Leaf(String name) {
this.name = name;
}
@Override
public void operation() {
System.out.println("Leaf: " + name + " operation");
}
}
// Composite
class Composite implements Component {
private List<Component> children = new ArrayList<>();
public void add(Component component) {
children.add(component);
}
public void remove(Component component) {
children.remove(component);
}
@Override
public void operation() {
System.out.println("Composite operation");
for (Component component : children) {
component.operation();
}
}
}
// Client
public class Main {
public static void main(String[] args) {
Component leaf1 = new Leaf("Leaf 1");
Component leaf2 = new Leaf("Leaf 2");
Component leaf3 = new Leaf("Leaf 3");
Composite composite1 = new Composite();
composite1.add(leaf1);
composite1.add(leaf2);
Composite composite2 = new Composite();
composite2.add(composite1);
composite2.add(leaf3);
composite2.operation();
}
}
Decorator
The Decorator pattern is a structural design pattern that allows behavior to be added to individual objects dynamically, without affecting the behavior of other objects from the same class.
// Component interface
interface Coffee {
String getDescription();
double cost();
}
// Concrete component
class SimpleCoffee implements Coffee {
@Override
public String getDescription() {
return "Simple coffee";
}
@Override
public double cost() {
return 1.0;
}
}
// Decorator
abstract class CoffeeDecorator implements Coffee {
protected Coffee decoratedCoffee;
public CoffeeDecorator(Coffee decoratedCoffee) {
this.decoratedCoffee = decoratedCoffee;
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription();
}
@Override
public double cost() {
return decoratedCoffee.cost();
}
}
// Concrete decorator
class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee decoratedCoffee) {
super(decoratedCoffee);
}
@Override
public String getDescription() {
return super.getDescription() + ", with milk";
}
@Override
public double cost() {
return super.cost() + 0.5;
}
}
// Concrete decorator
class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee decoratedCoffee) {
super(decoratedCoffee);
}
@Override
public String getDescription() {
return super.getDescription() + ", with sugar";
}
@Override
public double cost() {
return super.cost() + 0.2;
}
}
public class Main {
public static void main(String[] args) {
// Create a simple coffee
Coffee coffee = new SimpleCoffee();
System.out.println(coffee.getDescription() + ": $" + coffee.cost());
// Decorate the coffee with milk
coffee = new MilkDecorator(coffee);
System.out.println(coffee.getDescription() + ": $" + coffee.cost());
// Decorate the coffee with sugar
coffee = new SugarDecorator(coffee);
System.out.println(coffee.getDescription() + ": $" + coffee.cost());
}
}
Facade
The Facade pattern is a structural design pattern that provides a simplified interface to a complex subsystem of classes, making it easier to use.
// Complex subsystem
class CPU {
public void processData() {
System.out.println("CPU is processing data");
}
}
class Memory {
public void load() {
System.out.println("Memory is loading data");
}
}
class HardDrive {
public void readData() {
System.out.println("Hard Drive is reading data");
}
}
// Facade
class ComputerFacade {
private CPU cpu;
private Memory memory;
private HardDrive hardDrive;
public ComputerFacade() {
this.cpu = new CPU();
this.memory = new Memory();
this.hardDrive = new HardDrive();
}
public void start() {
cpu.processData();
memory.load();
hardDrive.readData();
System.out.println("Computer is started");
}
}
// Client code
public class Main {
public static void main(String[] args) {
ComputerFacade computer = new ComputerFacade();
computer.start();
}
}
Flyweight
The Flyweight pattern is a structural design pattern that aims to minimize memory usage and improve performance by sharing common state among multiple objects instead of keeping it in each object individually.
import java.util.HashMap;
import java.util.Map;
// Flyweight interface
interface Shape {
void draw();
}
// Concrete flyweight class
class Circle implements Shape {
private String color;
public Circle(String color) {
this.color = color;
}
@Override
public void draw() {
System.out.println("Drawing circle with color: " + color);
}
}
// Flyweight factory
class ShapeFactory {
private static final Map<String, Shape> circleMap = new HashMap<>();
public static Shape getCircle(String color) {
Circle circle = (Circle) circleMap.get(color);
if (circle == null) {
circle = new Circle(color);
circleMap.put(color, circle);
System.out.println("Creating circle of color: " + color);
}
return circle;
}
}
// Client code
public class Main {
private static final String[] colors = {"Red", "Green", "Blue"};
public static void main(String[] args) {
for (int i = 0; i < 10; ++i) {
Circle circle = (Circle) ShapeFactory.getCircle(getRandomColor());
circle.draw();
}
}
private static String getRandomColor() {
return colors[(int) (Math.random() * colors.length)];
}
}
Proxy
The Proxy pattern is a structural design pattern that provides a surrogate or placeholder for another object to control access to it. It allows you to add an additional layer of indirection to manage the interaction between the client and the real subject object.
// Subject interface
interface Image {
void display();
}
// Real subject
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk();
}
private void loadFromDisk() {
System.out.println("Loading image: " + filename);
}
@Override
public void display() {
System.out.println("Displaying image: " + filename);
}
}
// Proxy class
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();
}
}
// Client code
public class Main {
public static void main(String[] args) {
Image image1 = new ProxyImage("image1.jpg");
Image image2 = new ProxyImage("image2.jpg");
// Image1 will be loaded from disk
image1.display();
// Image1 will be displayed from memory (already loaded)
image1.display();
// Image2 will be loaded from disk
image2.display();
// Image2 will be displayed from memory (already loaded)
image2.display();
}
}
Last updated