├── creational ├── Singleton.scala ├── LazyInitialization.scala ├── FactoryMethod.scala ├── Singleton.java ├── LazyInitialization.java └── FactoryMethod.java ├── behavioral ├── ValueObject.scala ├── Strategy.scala ├── NullObject.scala ├── Command.scala ├── DependencyInjection.scala ├── NullObject.java ├── ChainOfResponsibility.scala ├── DependencyInjection.java ├── Command.java ├── ValueObject.java ├── Strategy.java └── ChainOfResponsibility.java ├── structural ├── Decorator.scala ├── Adapter.scala ├── Adapter.java └── Decorator.java └── README.md /creational/Singleton.scala: -------------------------------------------------------------------------------- 1 | object Cat extends Runnable { 2 | def run() { 3 | // do nothing 4 | } 5 | } 6 | 7 | Cat.run() 8 | -------------------------------------------------------------------------------- /creational/LazyInitialization.scala: -------------------------------------------------------------------------------- 1 | lazy val x = { 2 | print("(computing x) ") 3 | 42 4 | } 5 | 6 | print("x = ") 7 | println(x) 8 | 9 | // x = (computing x) 42 10 | -------------------------------------------------------------------------------- /behavioral/ValueObject.scala: -------------------------------------------------------------------------------- 1 | val point = (1, 2) // new Tuple2(1, 2) 2 | 3 | 4 | type Point = (Int, Int) // Tuple2[Int, Int] 5 | 6 | val point: Point = (1, 2) 7 | 8 | 9 | case class Point(x: Int, y: Int) 10 | 11 | val point = Point(1, 2) 12 | -------------------------------------------------------------------------------- /behavioral/Strategy.scala: -------------------------------------------------------------------------------- 1 | type Strategy = (Int, Int) => Int 2 | 3 | class Context(computer: Strategy) { 4 | def use(a: Int, b: Int) { computer(a, b) } 5 | } 6 | 7 | val add: Strategy = _ + _ 8 | val multiply: Strategy = _ * _ 9 | 10 | new Context(multiply).use(2, 3) 11 | -------------------------------------------------------------------------------- /creational/FactoryMethod.scala: -------------------------------------------------------------------------------- 1 | trait Animal 2 | private class Dog extends Animal 3 | private class Cat extends Animal 4 | 5 | object Animal { 6 | def apply(kind: String) = kind match { 7 | case "dog" => new Dog() 8 | case "cat" => new Cat() 9 | } 10 | } 11 | 12 | Animal("dog") 13 | -------------------------------------------------------------------------------- /behavioral/NullObject.scala: -------------------------------------------------------------------------------- 1 | trait Sound { 2 | def play() 3 | } 4 | 5 | class Music extends Sound { 6 | def play() { /* ... */ } 7 | } 8 | 9 | object SoundSource { 10 | def getSound: Option[Sound] = 11 | if (available) Some(music) else None 12 | } 13 | 14 | for (sound <- SoundSource.getSound) { 15 | sound.play() 16 | } 17 | -------------------------------------------------------------------------------- /behavioral/Command.scala: -------------------------------------------------------------------------------- 1 | object Invoker { 2 | private var history: Seq[() => Unit] = Seq.empty 3 | 4 | def invoke(command: => Unit) { // by-name parameter 5 | command 6 | history :+= command _ 7 | } 8 | } 9 | 10 | Invoker.invoke(println("foo")) 11 | 12 | Invoker.invoke { 13 | println("bar 1") 14 | println("bar 2") 15 | } 16 | -------------------------------------------------------------------------------- /creational/Singleton.java: -------------------------------------------------------------------------------- 1 | public class Cat implements Runnable { 2 | private static final Cat instance = new Cat(); 3 | 4 | private Cat() {} 5 | 6 | public void run() { 7 | // do nothing 8 | } 9 | 10 | public static Cat getInstance() { 11 | return instance; 12 | } 13 | } 14 | 15 | Cat.getInstance().run() 16 | -------------------------------------------------------------------------------- /behavioral/DependencyInjection.scala: -------------------------------------------------------------------------------- 1 | trait Repository { 2 | def save(user: User) 3 | } 4 | 5 | trait DatabaseRepository extends Repository { /* ... */ } 6 | 7 | trait UserService { self: Repository => // requires Repository 8 | def create(user: User) { 9 | // ... 10 | save(user) 11 | } 12 | } 13 | 14 | new UserService with DatabaseRepository 15 | -------------------------------------------------------------------------------- /creational/LazyInitialization.java: -------------------------------------------------------------------------------- 1 | private volatile Component component; 2 | 3 | public Component getComponent() { 4 | Component result = component; 5 | if (result == null) { 6 | synchronized(this) { 7 | result = component; 8 | if (result == null) { 9 | component = result = new Component(); 10 | } 11 | } 12 | } 13 | return result; 14 | } 15 | -------------------------------------------------------------------------------- /structural/Decorator.scala: -------------------------------------------------------------------------------- 1 | trait OutputStream { 2 | def write(b: Byte) 3 | def write(b: Array[Byte]) 4 | } 5 | 6 | class FileOutputStream(path: String) extends OutputStream { /* ... */ } 7 | 8 | trait Buffering extends OutputStream { 9 | abstract override def write(b: Byte) { 10 | // ... 11 | super.write(buffer) 12 | } 13 | } 14 | 15 | new FileOutputStream("foo.txt") with Buffering // with Filtering, ... 16 | -------------------------------------------------------------------------------- /behavioral/NullObject.java: -------------------------------------------------------------------------------- 1 | public interface Sound { 2 | void play(); 3 | } 4 | 5 | public class Music implements Sound { 6 | public void play() { /* ... */ } 7 | } 8 | 9 | public class NullSound implements Sound { 10 | public void play() {} 11 | } 12 | 13 | public class SoundSource { 14 | public static Sound getSound() { 15 | return available ? music : new NullSound(); 16 | } 17 | } 18 | 19 | SoundSource.getSound().play(); 20 | -------------------------------------------------------------------------------- /structural/Adapter.scala: -------------------------------------------------------------------------------- 1 | trait Log { 2 | def warning(message: String) 3 | def error(message: String) 4 | } 5 | 6 | final class Logger { 7 | def log(level: Level, message: String) { /* ... */ } 8 | } 9 | 10 | implicit class LoggerToLogAdapter(logger: Logger) extends Log { 11 | def warning(message: String) { logger.log(WARNING, message) } 12 | def error(message: String) { logger.log(ERROR, message) } 13 | } 14 | 15 | val log: Log = new Logger() 16 | -------------------------------------------------------------------------------- /behavioral/ChainOfResponsibility.scala: -------------------------------------------------------------------------------- 1 | case class Event(source: String) 2 | 3 | type EventHandler = PartialFunction[Event, Unit] 4 | 5 | val defaultHandler: EventHandler = PartialFunction(_ => ()) 6 | 7 | val keyboardHandler: EventHandler = { 8 | case Event("keyboard") => /* ... */ 9 | } 10 | 11 | def mouseHandler(delay: Int): EventHandler = { 12 | case Event("mouse") => /* ... */ 13 | } 14 | 15 | keyboardHandler.orElse(mouseHandler(100)).orElse(defaultHandler) 16 | -------------------------------------------------------------------------------- /creational/FactoryMethod.java: -------------------------------------------------------------------------------- 1 | public interface Animal {} 2 | 3 | private class Dog implements Animal {} 4 | 5 | private class Cat implements Animal {} 6 | 7 | public class AnimalFactory { 8 | public static Animal createAnimal(String kind) { 9 | if ("cat".equals(kind)) return new Cat(); 10 | if ("dog".equals(kind)) return new Dog(); 11 | throw new IllegalArgumentException(); 12 | } 13 | } 14 | 15 | AnimalFactory.createAnimal("dog"); 16 | -------------------------------------------------------------------------------- /behavioral/DependencyInjection.java: -------------------------------------------------------------------------------- 1 | public interface Repository { 2 | void save(User user); 3 | } 4 | 5 | public class DatabaseRepository implements Repository { /* ... */ } 6 | 7 | public class UserService { 8 | private final Repository repository; 9 | 10 | UserService(Repository repository) { 11 | this.repository = repository; 12 | } 13 | 14 | void create(User user) { 15 | // ... 16 | repository.save(user); 17 | } 18 | } 19 | 20 | new UserService(new DatabaseRepository()); 21 | -------------------------------------------------------------------------------- /behavioral/Command.java: -------------------------------------------------------------------------------- 1 | public class PrintCommand implements Runnable { 2 | private final String s; 3 | 4 | PrintCommand(String s) { this.s = s; } 5 | 6 | public void run() { 7 | System.out.println(s); 8 | } 9 | } 10 | 11 | public class Invoker { 12 | private final List history = new ArrayList<>(); 13 | 14 | void invoke(Runnable command) { 15 | command.run(); 16 | history.add(command); 17 | } 18 | } 19 | 20 | Invoker invoker = new Invoker(); 21 | invoker.invoke(new PrintCommand("foo")); 22 | invoker.invoke(new PrintCommand("bar")); 23 | -------------------------------------------------------------------------------- /behavioral/ValueObject.java: -------------------------------------------------------------------------------- 1 | public class Point { 2 | private final int x, y; 3 | 4 | public Point(int x, int y) { this.x = x; this.y = y; } 5 | 6 | public int getX() { return x; } 7 | 8 | public int getY() { return y; } 9 | 10 | public boolean equals(Object o) { 11 | // ... 12 | return x == that.x && y == that.y; 13 | } 14 | 15 | public int hashCode() { 16 | return 31 * x + y; 17 | } 18 | 19 | public String toString() { 20 | return String.format("Point(%d, %d)", x, y); 21 | } 22 | } 23 | 24 | Point point = new Point(1, 2) 25 | -------------------------------------------------------------------------------- /behavioral/Strategy.java: -------------------------------------------------------------------------------- 1 | public interface Strategy { 2 | int compute(int a, int b); 3 | } 4 | 5 | public class Add implements Strategy { 6 | public int compute(int a, int b) { return a + b; } 7 | } 8 | 9 | public class Multiply implements Strategy { 10 | public int compute(int a, int b) { return a * b; } 11 | } 12 | 13 | public class Context { 14 | private final Strategy strategy; 15 | 16 | public Context(Strategy strategy) { this.strategy = strategy; } 17 | 18 | public void use(int a, int b) { strategy.compute(a, b); } 19 | } 20 | 21 | new Context(new Multiply()).use(2, 3); 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Patterns README 2 | =============== 3 | 4 | Code examples for my article [Design Patterns in Scala](http://pavelfatin.com/design-patterns-in-scala/) (adoption and transformation of classical software design patterns in Scala). 5 | 6 | 7 | Creational patterns: 8 | 9 | * Factory method 10 | * Lazy initialization 11 | * Singleton 12 | 13 | Structural patterns: 14 | 15 | * Adapter 16 | * Decorator 17 | 18 | Behavioral patterns: 19 | 20 | * Value object 21 | * Null Object 22 | * Strategy 23 | * Command 24 | * Chain of responsibility 25 | * Dependency injection 26 | 27 | 28 | Pavel Fatin, [http://pavelfatin.com](http://pavelfatin.com/) 29 | -------------------------------------------------------------------------------- /structural/Adapter.java: -------------------------------------------------------------------------------- 1 | public interface Log { 2 | void warning(String message); 3 | void error(String message); 4 | } 5 | 6 | public final class Logger { 7 | void log(Level level, String message) { /* ... */ } 8 | } 9 | 10 | public class LoggerToLogAdapter implements Log { 11 | private final Logger logger; 12 | 13 | public LoggerToLogAdapter(Logger logger) { this.logger = logger; } 14 | 15 | public void warning(String message) { 16 | logger.log(WARNING, message); 17 | } 18 | 19 | public void error(String message) { 20 | logger.log(ERROR, message); 21 | } 22 | } 23 | 24 | Log log = new LoggerToLogAdapter(new Logger()); 25 | -------------------------------------------------------------------------------- /behavioral/ChainOfResponsibility.java: -------------------------------------------------------------------------------- 1 | public abstract class EventHandler { 2 | private EventHandler next; 3 | 4 | void setNext(EventHandler handler) { next = handler; } 5 | 6 | public void handle(Event event) { 7 | if (canHandle(event)) doHandle(event); 8 | else if (next != null) next.handle(event); 9 | } 10 | 11 | abstract protected boolean canHandle(Event event); 12 | abstract protected void doHandle(Event event); 13 | } 14 | 15 | public class KeyboardHandler extends EventHandler { // MouseHandler... 16 | protected boolean canHandle(Event event) { 17 | return "keyboard".equals(event.getSource()); 18 | } 19 | 20 | protected void doHandle(Event event) { /* ... */ } 21 | } 22 | 23 | KeyboardHandler handler = new KeyboardHandler(); 24 | handler.setNext(new MouseHandler()); 25 | -------------------------------------------------------------------------------- /structural/Decorator.java: -------------------------------------------------------------------------------- 1 | public interface OutputStream { 2 | void write(byte b); 3 | void write(byte[] b); 4 | } 5 | 6 | public class FileOutputStream implements OutputStream { /* ... */ } 7 | 8 | public abstract class OutputStreamDecorator implements OutputStream { 9 | protected final OutputStream delegate; 10 | 11 | protected OutputStreamDecorator(OutputStream delegate) { 12 | this.delegate = delegate; 13 | } 14 | 15 | public void write(byte b) { delegate.write(b); } 16 | public void write(byte[] b) { delegate.write(b); } 17 | } 18 | 19 | public class BufferedOutputStream extends OutputStreamDecorator { 20 | public BufferedOutputStream(OutputStream delegate) { 21 | super(delegate); 22 | } 23 | 24 | public void write(byte b) { 25 | // ... 26 | delegate.write(buffer) 27 | } 28 | } 29 | 30 | new BufferedOutputStream(new FileOutputStream("foo.txt")) 31 | --------------------------------------------------------------------------------