├── .classpath ├── .gitignore ├── .project ├── LICENSE ├── README.adoc ├── pom.xml └── src ├── main ├── java │ ├── behavioral │ │ ├── ChainOfResponsibilityDemo.java │ │ ├── ChainOfResponsibilityLambdas.java │ │ ├── GameStateDemo.java │ │ ├── ObserverDemo.java │ │ ├── ObserverPropChangeListenerDemo.java │ │ ├── PlayerStateDemo.java │ │ ├── bidpay │ │ │ ├── Auction.java │ │ │ ├── BidCommand.java │ │ │ ├── BidPaySite.java │ │ │ ├── CancelCommand.java │ │ │ ├── Client.java │ │ │ ├── Command.java │ │ │ └── CompositeCommand.java │ │ └── visitor │ │ │ ├── ImageCaptionNode.java │ │ │ ├── ImageNode.java │ │ │ ├── Node.java │ │ │ ├── TextNode.java │ │ │ ├── Visitor.java │ │ │ ├── WordCountVisitor.java │ │ │ ├── WordProcessorDemo.java │ │ │ └── file │ │ │ ├── FileVisitorDemo.java │ │ │ ├── IndentingFileVisitor.java │ │ │ └── TrivialListerVisitor.java │ ├── coding │ │ ├── ErrorHandling.java │ │ └── README.txt │ ├── creation │ │ ├── AbstractFactoryDemo.java │ │ ├── FactoryDemo.java │ │ ├── MessageRenderer.java │ │ ├── MyRenderer.java │ │ ├── ReloadingFactory.java │ │ ├── Singleton.java │ │ ├── SingletonDemo.java │ │ ├── SingletonEnum.java │ │ ├── SingletonLazy.java │ │ ├── SwingRenderer.java │ │ └── builder │ │ │ ├── Play.java │ │ │ ├── PlayDemo.java │ │ │ └── README.adoc │ └── structure │ │ ├── adapter │ │ ├── AdapterDemo.java │ │ ├── Lib1.java │ │ ├── Lib1Adapter.java │ │ ├── Lib2.java │ │ ├── Lib2Adapter.java │ │ ├── LibAdapter.java │ │ └── SimpleAdapter.java │ │ ├── decorator │ │ ├── DigitalImage.java │ │ ├── Frame.java │ │ ├── IOStreamsDemo.java │ │ ├── ImageDecorator.java │ │ ├── ImageSales.java │ │ ├── Mat.java │ │ ├── PhotoImage.java │ │ ├── Print.java │ │ ├── StockAgency.java │ │ ├── StreamDemo.java │ │ └── TransactionInterceptorPseudoCode.java │ │ └── proxy │ │ ├── CdiDemoSe.java │ │ ├── CdiLoggingInterceptor.java │ │ ├── DynamicProxyDemo.java │ │ ├── QuoteServer.java │ │ ├── QuoteServerImpl.java │ │ ├── ShoppingService.java │ │ ├── SimpleProxyDemo.java │ │ └── interceptors.adoc └── resources │ ├── META-INF │ └── beans.xml │ └── creation │ └── factory.config └── test ├── java ├── behavioral │ ├── bidpay │ │ └── CompositeCommandTest.java │ └── visitor │ │ └── WordCountVisitorTest.java └── structure │ └── proxy │ └── QuoteServerImplTest.java └── resources └── .placeholder /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .settings 3 | .idea 4 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | patterns-demo 4 | Code Examples for Design Patterns in Java 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.m2e.core.maven2Nature 22 | 23 | 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2010, 2012, 2016, Ian F. Darwin 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | Java, the Duke mascot, and all variants of Sun's Java "steaming coffee 28 | cup" logo are trademarks of Oracle Americas (formerly Sun Microsystems). 29 | James Gosling's pioneering role in inventing and promulgating Java, 30 | and Sun's and later Oracle's role in maintaining and "moving Java forward" 31 | is gratefully acknowledged. 32 | 33 | The pioneering roles of Dennis Ritchie and Bjarne Stroustrup, of AT&T, for 34 | inventing predecessor languages C and C++, are also gratefully acknowledged. 35 | 36 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = patterns-demos 2 | 3 | This repo contains some examples of Design Patterns in Java; it was split off from my main 4 | https://github.com/IanDarwin/javasrc[javasrc repo] to keep the latter from becoming bottom-heavy. 5 | 6 | == Readings 7 | 8 | The original book that popularized Design Patterns - after they were in use for a decade 9 | in the CS community - is 10 | https://www.amazon.com/dp/0201633612[Design Patterns: Elements of Reusable Software], 11 | by Gamma, Helm, Johnson and Vlissides, known more concisely as the Gang of Four or even the GoF. 12 | 13 | A less academic, more memorable, more _fun_ read is O'Reilly's 14 | https://www.amazon.ca/dp/0596007124[Head-First Design Patterns]. 15 | 16 | == My Writings 17 | 18 | The OO chapter of my https://www.amazon.ca/dp/144933704X[Java Cookbook], 3rd edition. 19 | 20 | Command Pattern, in Oracle's Java Magazine May/June 2018. 21 | https://blogs.oracle.com/javamagazine/the-command-pattern-in-depth 22 | 23 | State Pattern, in Oracle's Java Magazine, July/August 2018. 24 | https://blogs.oracle.com/javamagazine/the-state-pattern 25 | 26 | Visitor Pattern, in Oracle's Java Magazine, September/October 2018. 27 | https://blogs.oracle.com/javamagazine/the-visitor-design-pattern-in-depth 28 | 29 | Decorator Pattern, in Oracle's Java Magazine, November/December 2018. 30 | https://blogs.oracle.com/javamagazine/the-decorator-pattern-in-depth 31 | 32 | Proxy Pattern, in Oracle's Java Magazine, March/April 2019 33 | https://blogs.oracle.com/javamagazine/the-proxy-pattern 34 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.darwinsys 7 | patterns-demos 8 | 1.0-SNAPSHOT 9 | jar 10 | 1995 11 | 12 | patterns-demos 13 | https://darwinsys.com/java 14 | Code Examples for the Design Patterns in Java Demos 15 | 16 | 17 | UTF-8 18 | UTF-8 19 | 8 20 | ${javase.version} 21 | ${javase.version} 22 | ${javase.version} 23 | ${javase.version} 24 | 25 | 26 | 27 | 28 | 29 | com.darwinsys 30 | darwinsys-api 31 | 1.7.6 32 | 33 | 34 | 35 | 36 | javax.enterprise 37 | cdi-api 38 | 2.0 39 | compile 40 | 41 | 42 | org.jboss.weld.se 43 | weld-se-shaded 44 | 3.0.1.Final 45 | 46 | 47 | 48 | 49 | 50 | junit 51 | junit 52 | 4.13.1 53 | 54 | 55 | org.mockito 56 | mockito-all 57 | 1.10.19 58 | test 59 | 60 | 61 | org.hamcrest 62 | hamcrest-all 63 | 1.3 64 | test 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/main/java/behavioral/ChainOfResponsibilityDemo.java: -------------------------------------------------------------------------------- 1 | package behavioral; 2 | 3 | /** A simple demo of ChainOfResponsibility, 4 | * a GoF Design Pattern. 5 | */ 6 | enum Service { PURCHASE, DISCOUNT, REFUND } 7 | 8 | abstract class Employee { 9 | Employee next; // Next employee up the chain 10 | public void setNext(Employee next) { 11 | this.next = next; 12 | } 13 | public abstract void processCustomer(Service s); 14 | } 15 | 16 | class Cashier extends Employee { 17 | @Override 18 | public void processCustomer(Service s) { 19 | if (s == Service.PURCHASE) { 20 | System.out.println("Cashier handled " + s); 21 | return; 22 | } 23 | next.processCustomer(s); 24 | } 25 | } 26 | 27 | class Supervisor extends Employee { 28 | 29 | @Override 30 | public void processCustomer(Service s) { 31 | if (s == Service.PURCHASE || s == Service.DISCOUNT) { 32 | System.out.println("Supervisor handled " + s); 33 | return; 34 | } 35 | next.processCustomer(s); 36 | } 37 | } 38 | 39 | class StoreManager extends Employee { 40 | @Override 41 | public void processCustomer(Service s) { 42 | System.out.println("Manager handled " + s); 43 | } 44 | 45 | } 46 | 47 | public class ChainOfResponsibilityDemo { 48 | public static void main(String[] args) { 49 | Employee s3 = new StoreManager(); 50 | Employee s2 = new Supervisor(); 51 | s2.setNext(s3); 52 | Employee s1 = new Cashier(); 53 | s1.setNext(s2); 54 | 55 | s1.processCustomer(Service.PURCHASE); 56 | s1.processCustomer(Service.DISCOUNT); 57 | s1.processCustomer(Service.REFUND); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/behavioral/ChainOfResponsibilityLambdas.java: -------------------------------------------------------------------------------- 1 | package behavioral; 2 | 3 | import java.util.function.*; 4 | 5 | /** 6 | * Change of Responsibility, simple example using lambdas 7 | * that are public static so testable independently. 8 | * @author Adapted from an example by Richard Warburton 9 | */ 10 | public class ChainOfResponsibilityLambdas { 11 | static UnaryOperator headerProcessing = 12 | (String text) -> "From Raoul, Richard: " + text; 13 | 14 | static UnaryOperator spellCheckerProcessing = 15 | (String text) -> text.replaceAll("lamda", "lambda"); 16 | 17 | static UnaryOperator uploadProcessing = 18 | (String text) -> { System.out.println("Uploading: " + text); return text; }; 19 | 20 | public static void main(String[] args) { 21 | Function pipeline = headerProcessing 22 | .andThen(spellCheckerProcessing) 23 | .andThen(uploadProcessing); 24 | 25 | String result = pipeline.apply("Aren't lamdas really cool?!!"); 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /src/main/java/behavioral/GameStateDemo.java: -------------------------------------------------------------------------------- 1 | package behavioral; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | 7 | /** 8 | * A simple demo of using a State machine implemented as Java classes. 9 | * A text-mode game, and, not intended to be a full RPG game, sorry, 10 | * but at the beginning of (computer) time, the only games were text games 11 | * like Crowley's Adventure and later, Michael Toy's curses-based Rogue. 12 | * This is NOT the only way to implement state machines; 13 | * they have been done for years in many languages as 14 | * e.g., transition tables of integers, pointers to structures, etc. 15 | */ 16 | public class GameStateDemo { 17 | 18 | /** 19 | * Game on! 20 | */ 21 | public static void main(String[] args) throws IOException { 22 | display("Welcome to the game"); 23 | doHelp(); 24 | GameStateDemo game = new GameStateDemo(); 25 | game.state.lookAround(); 26 | game.play(); 27 | } 28 | 29 | enum Command { LOOK, ENTER, EXIT, QUIT } 30 | 31 | void enterState(State state) { 32 | this.state = state; 33 | state.lookAround(); 34 | } 35 | 36 | abstract class State { 37 | public abstract void lookAround(); 38 | public abstract void goInside(); 39 | public abstract void goOutside(); 40 | public void quitGame() { 41 | // In this trivial game it makes sense to allow exit 42 | // from any state, so allow that here. 43 | // In a real game, should not quit without e.g., 44 | // prompting the user if they're holding any valuables. 45 | display("Goodbye!"); 46 | System.exit(0); 47 | } 48 | } 49 | 50 | public State outdoorsState = new State() { 51 | 52 | @Override 53 | public void lookAround() { 54 | display("You are in a clearing in the Unchanging Woods. There is a little cabin."); 55 | } 56 | 57 | @Override 58 | public void goInside() { 59 | enterState(inHallwayState); 60 | } 61 | 62 | @Override 63 | public void goOutside() { 64 | display("You are already outside, fool!"); 65 | } 66 | }; 67 | 68 | public State inHallwayState = new State() { 69 | public void lookAround() { 70 | display("You are in a hallway. There is a door here"); 71 | } 72 | public void goInside() { 73 | enterState(inRoomState); 74 | } 75 | public void goOutside() { 76 | display("You are leaving the cabin."); 77 | enterState(outdoorsState); 78 | } 79 | }; 80 | 81 | public State inRoomState = new State() { 82 | public void lookAround() { 83 | // This would map to the description of a particular room 84 | display("The room is full of gold!"); 85 | } 86 | public void goInside() { 87 | display("You are already in the room"); 88 | } 89 | public void goOutside() { 90 | enterState(inHallwayState); 91 | } 92 | }; 93 | 94 | public void oneMove(String line) { 95 | try { 96 | Command c = Command.valueOf(line.toUpperCase()); 97 | switch(c) { 98 | case LOOK: state.lookAround(); break; 99 | case ENTER: state.goInside(); break; 100 | case EXIT: state.goOutside(); break; 101 | case QUIT: state.quitGame(); break; 102 | } 103 | } catch (IllegalArgumentException e) { 104 | display("I don't quite follow you."); 105 | doHelp(); 106 | } 107 | } 108 | 109 | private static void doHelp() { 110 | display("I know but a few actions:"); 111 | for (Command c : Command.values()) { 112 | System.out.print(c.name()); 113 | System.out.print(' '); 114 | } 115 | System.out.println(); 116 | } 117 | 118 | public static void display(String mesg) { 119 | System.out.println(mesg); 120 | } 121 | 122 | private final State INITIAL_STATE = outdoorsState; 123 | 124 | private State state = INITIAL_STATE; 125 | 126 | public void play() throws IOException { 127 | BufferedReader is = new BufferedReader(new InputStreamReader(System.in)); 128 | String line; 129 | 130 | while ((line = is.readLine()) != null) { 131 | oneMove(line); 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/behavioral/ObserverDemo.java: -------------------------------------------------------------------------------- 1 | package behavioral; 2 | 3 | import java.util.Observable; 4 | import java.util.Observer; 5 | 6 | /** 7 | * A simple demo of Observable->Observer 8 | * Note that the actual Observer and Observable types are deprecated as of Java 9; 9 | * see the ObserverPropChangeListenerDemo instead of this class. 10 | * @author Ian Darwin 11 | */ 12 | @SuppressWarnings("deprecation") 13 | public class ObserverDemo extends Object { 14 | MyView view; 15 | MyModel model; 16 | 17 | public ObserverDemo() { 18 | 19 | view = new MyView(); 20 | model = new MyModel(); 21 | model.addObserver(view); 22 | } 23 | 24 | public static void main(String[] av) { 25 | ObserverDemo me = new ObserverDemo(); 26 | me.runTheMainApplication(); 27 | } 28 | 29 | /** Represents the main part of an application */ 30 | public void runTheMainApplication() { 31 | model.changeSomething(); 32 | } 33 | 34 | /** The Observer normally maintains a view on the data */ 35 | class MyView implements Observer { 36 | /** For now, we just print the fact that we got notified. */ 37 | @Override 38 | public void update( Observable obs, Object x ) { 39 | System.out.println("update(" + obs + "," + x + ");"); 40 | } 41 | } 42 | 43 | /** The Observable normally maintains the data */ 44 | class MyModel extends Observable { 45 | public void changeSomething() { 46 | // Notify observers of change 47 | setChanged(); 48 | notifyObservers(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/behavioral/ObserverPropChangeListenerDemo.java: -------------------------------------------------------------------------------- 1 | package behavioral; 2 | 3 | import java.beans.*; 4 | 5 | /** 6 | * A simple demo of Observable->Observer using JavaBeans PropertyChangeListener 7 | * 8 | * @author Ian Darwin 9 | */ 10 | public class ObserverPropChangeListenerDemo extends Object { 11 | MyView view; 12 | MyModel model; 13 | 14 | public ObserverPropChangeListenerDemo() { 15 | view = new MyView(); 16 | model = new MyModel(); 17 | model.addObserver(view); 18 | } 19 | 20 | public static void main(String[] av) { 21 | ObserverPropChangeListenerDemo me = new ObserverPropChangeListenerDemo(); 22 | me.runTheMainApplication(); 23 | } 24 | 25 | /** Represents the main part of an application */ 26 | public void runTheMainApplication() { 27 | model.changeSomething("A new value"); 28 | model.changeSomething("Another new value"); 29 | } 30 | 31 | /** The Observer normally maintains a view on the data */ 32 | class MyView implements PropertyChangeListener { 33 | /** For now, we just print the fact that we got notified. */ 34 | @Override 35 | public void propertyChange(PropertyChangeEvent ev) { 36 | System.out.printf("update(%s->%s);\n", ev.getOldValue(), ev.getNewValue()); 37 | } 38 | } 39 | 40 | /** The Observable normally maintains the data */ 41 | class MyModel { 42 | PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); 43 | String state = "Initial state"; 44 | 45 | public void addObserver(PropertyChangeListener pcl) { 46 | propertyChangeSupport.addPropertyChangeListener(pcl); 47 | } 48 | 49 | public void changeSomething(String newValue) { 50 | // Notify observers of change 51 | propertyChangeSupport.firePropertyChange("something", state, newValue); 52 | state = newValue; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/behavioral/PlayerStateDemo.java: -------------------------------------------------------------------------------- 1 | package behavioral; 2 | 3 | interface State { 4 | abstract void stop(); 5 | abstract void start(); 6 | abstract void pause(); 7 | abstract void rewind(); 8 | default void enterState() { 9 | // Only some states will need this 10 | } 11 | } 12 | 13 | /** 14 | * Model a simple media player 15 | * Doesn't actually play media yet 16 | * (use existing Java classes for that) 17 | */ 18 | public class PlayerStateDemo implements State { 19 | 20 | ////////////////////////////////////////////// 21 | // Delegate operations to current State object 22 | ////////////////////////////////////////////// 23 | public void enterState() { 24 | currentState.enterState(); 25 | } 26 | public void stop() { 27 | currentState.stop(); 28 | } 29 | 30 | public void start() { 31 | currentState.start(); 32 | } 33 | 34 | @Override 35 | public void pause() { 36 | currentState.pause(); 37 | } 38 | 39 | @Override 40 | public void rewind() { 41 | currentState.rewind(); 42 | } 43 | 44 | //////////////////////////////// 45 | // Define the State classes here 46 | //////////////////////////////// 47 | 48 | State stoppedState = new State() { 49 | @Override 50 | public void enterState() { 51 | stop(); 52 | // setIcon(Icon.stopped); 53 | } 54 | @Override 55 | public void stop() { 56 | // Do nothing 57 | } 58 | 59 | @Override 60 | public void start() { 61 | currentState = playingState; 62 | currentState.enterState(); 63 | } 64 | 65 | @Override 66 | public void pause() { 67 | // Do nothing, already stopped 68 | } 69 | 70 | @Override 71 | public void rewind() { 72 | resetToStart(); 73 | } 74 | }; 75 | 76 | State playingState = new State() { 77 | 78 | public void enterState() { 79 | start(); 80 | // setIcon(Icon.PLAYING); 81 | } 82 | 83 | @Override 84 | public void stop() { 85 | currentState = stoppedState; 86 | currentState.enterState(); 87 | } 88 | 89 | @Override 90 | public void start() { 91 | // Do nothing 92 | } 93 | 94 | @Override 95 | public void pause() { 96 | stop(); 97 | } 98 | 99 | @Override 100 | public void rewind() { 101 | stop(); 102 | resetToStart(); 103 | start(); 104 | } 105 | }; 106 | 107 | State pausedState = new State() { 108 | 109 | @Override 110 | public void stop() { 111 | currentState = stoppedState; 112 | currentState.enterState(); 113 | } 114 | 115 | @Override 116 | public void start() { 117 | currentState = playingState; 118 | currentState.enterState(); 119 | } 120 | 121 | @Override 122 | public void pause() { 123 | // Do nothing 124 | } 125 | 126 | @Override 127 | public void rewind() { 128 | resetToStart(); 129 | } 130 | }; 131 | 132 | State rewindState = new State() { 133 | 134 | @Override 135 | public void stop() { 136 | currentState = stoppedState; 137 | currentState.enterState(); 138 | } 139 | 140 | @Override 141 | public void start() { 142 | // On mechanical transports we have to stop before going into play mode 143 | stop(); 144 | currentState = playingState; 145 | currentState.enterState(); 146 | } 147 | 148 | @Override 149 | public void pause() { 150 | currentState = pausedState; 151 | currentState.enterState(); 152 | } 153 | 154 | @Override 155 | public void rewind() { 156 | // Do nothing 157 | } 158 | 159 | public void enterState() { 160 | rewind(); 161 | } 162 | }; 163 | 164 | State currentState = stoppedState; 165 | 166 | // Non-fully-encapsulation version of getState(). 167 | // public State getState() { return currentState; } 168 | 169 | // "mild encapsulation"? version. Only reveal state name. 170 | // Might make non-public if it is only for diagnostic use. 171 | public String getState() { 172 | return currentState.getClass().getName(); 173 | } 174 | 175 | // This section shows the legacy, non-patterns way of implementing 176 | // one of the four methods. 177 | enum StateName { STOPPED, PLAYING, PAUSED, REWINDING } 178 | StateName currentStateName; 179 | public void unmaintainableStart() { 180 | if (currentStateName == StateName.STOPPED) { 181 | currentStateName = StateName.PLAYING; 182 | startPlay(); 183 | } else if (currentStateName == StateName.PAUSED) { 184 | currentStateName = StateName.PLAYING; 185 | resumePlay(); 186 | } else if (currentStateName == StateName.PLAYING) { 187 | System.out.println("Already playing!"); 188 | } else if (currentStateName == StateName.REWINDING) { 189 | System.out.println("Wait a while, OK?"); 190 | } 191 | } 192 | 193 | // Low level, hardware control 194 | void startPlay() {} 195 | void resumePlay() {} 196 | void resetToStart() {} 197 | 198 | // Demo program 199 | public static void main(String[] args) { 200 | PlayerStateDemo context = new PlayerStateDemo(); 201 | System.out.println("Initial state: " + context.getState()); 202 | // User presses the Start button 203 | context.start(); 204 | System.out.println("Current state: " + context.getState()); 205 | // User presses the Stop button 206 | context.stop(); 207 | System.out.println("Current state: " + context.getState()); 208 | // You get the idea 209 | context.rewind(); 210 | System.out.println("Current state: " + context.getState()); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/main/java/behavioral/bidpay/Auction.java: -------------------------------------------------------------------------------- 1 | package behavioral.bidpay; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * This represents one item that is up for auction on a bidding site like eBay.com. 7 | * Code is very simplistic, and is also not thread safe. 8 | */ 9 | public class Auction { 10 | String description; 11 | double minBid; 12 | Client seller; 13 | Bid highBid; 14 | List bids = new ArrayList<>(); 15 | // If canceled 16 | Client cancelBy; 17 | String cancelReason; 18 | 19 | private class Bid { 20 | double amount; 21 | Client bidder; 22 | Bid(double amount, Client bidder) { 23 | this.amount = amount; 24 | this.bidder = bidder; 25 | } 26 | } 27 | 28 | public Auction(String description, Client seller, double minBid) { 29 | this.description = description; 30 | this.seller = seller; 31 | this.minBid = minBid; 32 | } 33 | 34 | public boolean isCancelled() { 35 | return cancelBy != null; 36 | } 37 | 38 | /** Submit a bid */ 39 | public void bid(double amount, Client bidder) { 40 | if (amount < minBid) { 41 | System.out.printf("REJECT bid from %s for %s, must be %.2f\n", bidder, description, amount); 42 | return; 43 | } 44 | if (isCancelled()) { 45 | System.out.printf("REJECT bid from %s for CANCELED auction %s\n", bidder, description); 46 | return; 47 | } 48 | bids.add(new Bid(amount, bidder)); 49 | System.out.printf("Accepted bid of %.2f from %s for auction of %s\n", amount, bidder, description); 50 | Bid highBid = null; 51 | double highBidAmt = 0; 52 | for (Bid bid : bids) { 53 | if (bid.amount > highBidAmt) { 54 | highBidAmt = bid.amount; 55 | highBid = bid; 56 | } 57 | } 58 | this.highBid = highBid; 59 | } 60 | 61 | /** Cancel an Auction, irrevocably */ 62 | public void cancel(Client cnxer, String reason) { 63 | this.cancelBy = cnxer; 64 | this.cancelReason = reason; 65 | } 66 | 67 | public String toString() { 68 | if (isCancelled()) { 69 | return String.format("CANCELLED: %s's auction of %s", seller, description); 70 | } 71 | if (highBid == null) { 72 | return String.format("UNSOLD: %s's auction of %s did not get %.2f", 73 | seller, description, minBid); 74 | } else { 75 | return String.format("Will Sell: %s's auction of %s, high bid %.2f", 76 | seller, description, highBid.amount); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/behavioral/bidpay/BidCommand.java: -------------------------------------------------------------------------------- 1 | package behavioral.bidpay; 2 | 3 | public class BidCommand implements Command { 4 | Auction receiver; 5 | double amount; 6 | Client bidder; 7 | 8 | public BidCommand(Auction receiver, double amount, Client bidder) { 9 | this.receiver = receiver; 10 | this.amount = amount; 11 | this.bidder = bidder; 12 | } 13 | public void execute() { 14 | receiver.bid(amount, bidder); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/behavioral/bidpay/BidPaySite.java: -------------------------------------------------------------------------------- 1 | package behavioral.bidpay; 2 | 3 | public class BidPaySite { 4 | public static void main(String[] args) { 5 | new BidPaySite().process(); 6 | } 7 | 8 | /** A non-static control method */ 9 | void process() { 10 | System.out.println("----- BidPaySite V -1.-1.-1 here... -----"); 11 | fakeClientSendSomeBids(); 12 | printBidStatuses(); 13 | } 14 | 15 | /** Fake list of Clients */ 16 | static final Client[] clients = { 17 | new Client("BidPay Site"), 18 | new Client("MaryBeth"), 19 | new Client("Ian"), 20 | new Client("Keener"), 21 | new Client("El Cheapo"), 22 | new Client("ScamOMatic"), 23 | }; 24 | 25 | /** Fake list of auctions */ 26 | static final Auction[] auctions = { 27 | new Auction("Persian Rug", clients[1], 1000), 28 | new Auction("Mac Computer", clients[2], 1000), 29 | new Auction("Nikon D850", clients[3], 4000), 30 | new Auction("Tesla Model 3", clients[5], 50000), 31 | }; 32 | 33 | /** The name of this method tells you what's going on */ 34 | public void fakeClientSendSomeBids() { 35 | submitCommand(new BidCommand(auctions[0], 1001, clients[2])); 36 | submitCommand(new BidCommand(auctions[3], 30000, clients[4])); 37 | submitCommand(new CancelCommand(auctions[3], clients[0], "Above your pay grade")); 38 | submitCommand(new BidCommand(auctions[3], 50000, clients[4])); 39 | } 40 | 41 | /** IRL the real clients would invoke this method directly */ 42 | public void submitCommand(Command command) { 43 | // These could go into a queue to serialize them, or we could make sure 44 | // that the methods exposed to the Command are all thread-safe. 45 | // For now we just let the command do its thing: 46 | command.execute(); 47 | } 48 | 49 | public void printBidStatuses() { 50 | System.out.println("----- Current Status -----"); 51 | for (Auction a : auctions) { 52 | System.out.println(a); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/behavioral/bidpay/CancelCommand.java: -------------------------------------------------------------------------------- 1 | package behavioral.bidpay; 2 | 3 | public class CancelCommand implements Command { 4 | Auction receiver; 5 | Client cancelBy; 6 | String cancelReason; 7 | 8 | public CancelCommand(Auction receiver, Client cnxer, String reason) { 9 | this.receiver = receiver; 10 | this.cancelBy = cnxer; 11 | this.cancelReason = reason; 12 | } 13 | public void execute() { 14 | receiver.cancel(cancelBy, cancelReason); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/behavioral/bidpay/Client.java: -------------------------------------------------------------------------------- 1 | package behavioral.bidpay; 2 | 3 | /** 4 | * A Client can buy, sell, or both. 5 | */ 6 | public class Client { 7 | String name; 8 | public Client(String name) { 9 | this.name = name; 10 | } 11 | public String toString() { 12 | return name; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/behavioral/bidpay/Command.java: -------------------------------------------------------------------------------- 1 | package behavioral.bidpay; 2 | 3 | public interface Command { 4 | void execute(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/behavioral/bidpay/CompositeCommand.java: -------------------------------------------------------------------------------- 1 | package behavioral.bidpay; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * A CompositeCommand bundles multiple other Commands inside it. 7 | * It both is-a Command and has-a (list of) Command. 8 | */ 9 | class CompositeCommand implements Command { 10 | List commands; 11 | public CompositeCommand(List commands) { 12 | this.commands = commands; 13 | } 14 | public void execute() { 15 | commands.forEach(Command::execute); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/behavioral/visitor/ImageCaptionNode.java: -------------------------------------------------------------------------------- 1 | package behavioral.visitor; 2 | 3 | public class ImageCaptionNode extends TextNode{ 4 | 5 | // This is for further discussion! 6 | // E.g., might have a Figure Number associated. 7 | 8 | public ImageCaptionNode(String caption) { 9 | super(caption); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/behavioral/visitor/ImageNode.java: -------------------------------------------------------------------------------- 1 | package behavioral.visitor; 2 | 3 | /** 4 | * Simple bitmap image node. 5 | */ 6 | public class ImageNode implements Node { 7 | String fileName; 8 | byte[] imageData; 9 | int height, width; 10 | ImageCaptionNode caption; 11 | 12 | public ImageNode(String fileName, String caption) { 13 | load(fileName); 14 | this.caption = new ImageCaptionNode(caption); 15 | } 16 | 17 | private void load(String fileName) { 18 | // dummy for now 19 | this.fileName = fileName; 20 | height = width = 64; 21 | } 22 | 23 | @Override 24 | public void accept(Visitor v) { 25 | v.visitImageNode(this); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/behavioral/visitor/Node.java: -------------------------------------------------------------------------------- 1 | package behavioral.visitor; 2 | 3 | public interface Node { 4 | abstract void accept(Visitor v); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/behavioral/visitor/TextNode.java: -------------------------------------------------------------------------------- 1 | package behavioral.visitor; 2 | 3 | /** 4 | * Simple text node. 5 | */ 6 | public class TextNode implements Node { 7 | private StringBuilder text = new StringBuilder(); 8 | 9 | public TextNode() { 10 | // empty 11 | } 12 | 13 | public TextNode(String s) { 14 | // Here we know the SB exists and is empty 15 | text.append(s); 16 | } 17 | 18 | public String getText() { 19 | return text.toString(); 20 | } 21 | 22 | public void setText(String text) { 23 | this.text.setLength(0); this.text.append(text); 24 | } 25 | 26 | // Lots of function methods here - dummied out for now 27 | public int wordCount() { 28 | // Delegate to external wordcount module 29 | return 0; 30 | } 31 | public String getAsDraft() { 32 | // Break text into chunks of up to 72 chars, then print 33 | return null; 34 | } 35 | 36 | /** The actual Visitor acceptor */ 37 | public void accept(Visitor v) { 38 | v.visitTextNode(this); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/behavioral/visitor/Visitor.java: -------------------------------------------------------------------------------- 1 | package behavioral.visitor; 2 | 3 | /** 4 | * Visitors have to know how to visit every main kind of Node; 5 | */ 6 | public abstract class Visitor { 7 | 8 | public abstract void visitTextNode(TextNode textNode); 9 | 10 | public abstract void visitImageNode(ImageNode imageNode); 11 | 12 | // And so on for TableNode, SectionNode, VideoNode, etc. 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/behavioral/visitor/WordCountVisitor.java: -------------------------------------------------------------------------------- 1 | package behavioral.visitor; 2 | 3 | /** 4 | * Count the words in all the nodes. 5 | * Use for a single pass through the nodes only. 6 | */ 7 | public class WordCountVisitor extends Visitor { 8 | 9 | int wordCount = 0; 10 | 11 | @Override 12 | public void visitTextNode(TextNode textNode) { 13 | wordCount += wordCount(textNode.getText()); 14 | } 15 | 16 | public int getWordCount() { 17 | return wordCount; 18 | } 19 | 20 | private int wordCount(String text) { 21 | if (text == null || (text = text.trim()).length() == 0) 22 | return 0; 23 | // Replace all non-space chars with nothing; 24 | // add one because "hello word" has one space, is two words. 25 | return text.trim().replaceAll("[^\\s]", "").length() + 1; 26 | } 27 | 28 | @Override 29 | public void visitImageNode(ImageNode imageNode) { 30 | // You might say there's nothing to do, but let's try this: 31 | if (imageNode.caption != null) { 32 | visitTextNode(imageNode.caption); 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/behavioral/visitor/WordProcessorDemo.java: -------------------------------------------------------------------------------- 1 | package behavioral.visitor; 2 | 3 | import java.io.PrintWriter; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import java.util.stream.Stream; 7 | 8 | import com.darwinsys.formatting.Fmt; 9 | 10 | public class WordProcessorDemo { 11 | 12 | public static void main(String[] args) { 13 | List nodes = new ArrayList<>(); 14 | nodes.add(new TextNode("My Thesis Dissertation")); 15 | nodes.add(new TextNode("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")); 16 | nodes.add(new ImageNode("ian-pro-headshot.png", "Ian May Not Be As Advertised")); 17 | 18 | out = new PrintWriter(System.out); 19 | for (Node n : nodes) { 20 | n.accept(draftPrinterVisitor); 21 | } 22 | out.flush(); 23 | 24 | Visitor wordCountVisitor = new WordCountVisitor(); 25 | for (Node n : nodes) { 26 | n.accept(wordCountVisitor); 27 | } 28 | System.out.printf("The document has approximately %d words%n", ((WordCountVisitor) wordCountVisitor).getWordCount()); 29 | } 30 | 31 | static PrintWriter out; 32 | 33 | static Visitor draftPrinterVisitor = new Visitor() { 34 | @Override 35 | public void visitTextNode(TextNode textNode) { 36 | String[] lines = { textNode.getText() }; 37 | Fmt.format(Stream.of(lines), out); 38 | } 39 | 40 | @Override 41 | public void visitImageNode(ImageNode imageNode) { 42 | String caption = imageNode.caption != null ? imageNode.caption.getText() : "no caption"; 43 | System.out.printf("Image: name='%s', caption='%s'%n", imageNode.fileName, caption); 44 | } 45 | 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/behavioral/visitor/file/FileVisitorDemo.java: -------------------------------------------------------------------------------- 1 | package behavioral.visitor.file; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.FileVisitor; 5 | import java.nio.file.Files; 6 | import java.nio.file.Path; 7 | import java.nio.file.Paths; 8 | 9 | /** 10 | * Example Visitor pattern applied to listing directories, using standard java.nio classes. 11 | * Here the "nodes" are actual filesystem nodes (represented by "inodes" in the Unix/Linux sense) 12 | * @author Ian Darwin 13 | */ 14 | public class FileVisitorDemo { 15 | 16 | public static void main(String[] args) throws IOException { 17 | 18 | // Set the starting path 19 | Path startingPath = Paths.get("."); 20 | 21 | // Instantiate the Visitor object 22 | FileVisitor visitor; 23 | visitor = new TrivialListerVisitor(); 24 | // visitor = new IndentingFileVisitor(); 25 | 26 | // Use the built-in walkFileTree client to visit all directory,file nodes 27 | Files.walkFileTree(startingPath, visitor); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/behavioral/visitor/file/IndentingFileVisitor.java: -------------------------------------------------------------------------------- 1 | package behavioral.visitor.file; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.FileVisitResult; 5 | import java.nio.file.Path; 6 | import java.nio.file.SimpleFileVisitor; 7 | import java.nio.file.attribute.BasicFileAttributes; 8 | 9 | /** 10 | * A simple indentation-based directory tree lister. 11 | * Works, but since it's depth-first, entries in a directory may (depending on 12 | * the underlying filesystem) appear after the contents of one of its subdirectories. 13 | * To fix this might involve a stack of List that is created in preVisitDirectory(), 14 | * added to in visitFile(), and printed out in postVisitDirectory(). This would be somewhat 15 | * beyond what's needed to show the operation of the SimpleFileVistor. 16 | */ 17 | class IndentingFileVisitor extends SimpleFileVisitor { 18 | int indent = 0; 19 | 20 | @Override 21 | public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { 22 | System.out.println(dir); 23 | indent += 4; 24 | return FileVisitResult.CONTINUE; 25 | } 26 | 27 | @Override 28 | public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { 29 | for (int i = 0; i < indent; i++) { 30 | System.out.print(' '); 31 | } 32 | System.out.println(file.getFileName()); 33 | return FileVisitResult.CONTINUE; 34 | } 35 | 36 | @Override 37 | public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { 38 | indent -= 4; 39 | return FileVisitResult.CONTINUE; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/behavioral/visitor/file/TrivialListerVisitor.java: -------------------------------------------------------------------------------- 1 | package behavioral.visitor.file; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.FileVisitResult; 5 | import java.nio.file.Path; 6 | import java.nio.file.SimpleFileVisitor; 7 | import java.nio.file.attribute.BasicFileAttributes; 8 | 9 | public class TrivialListerVisitor extends SimpleFileVisitor { 10 | 11 | @Override 12 | public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { 13 | System.out.println("Start directory " + dir); 14 | return FileVisitResult.CONTINUE; 15 | } 16 | 17 | @Override 18 | public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { 19 | System.out.println(file.getFileName()); 20 | return FileVisitResult.CONTINUE; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/coding/ErrorHandling.java: -------------------------------------------------------------------------------- 1 | package coding; 2 | 3 | /** 4 | * Error handling generally belongs in user-interface code, not 5 | * in lower-level code. UI code often has to keep going in the face of 6 | * errors from lower level code. So, it's generally better for UI 7 | * code to catch errors inside a loop, rather than at the top 8 | * level; the antiPattern shown here terminates on the first error. 9 | */ 10 | public class ErrorHandling { 11 | 12 | /** 13 | * Just an example of a lower-level code that, if you call it enough, 14 | * will throw multiple Exceptions. 15 | */ 16 | void exampleOfLibraryCode(int i) throws Exception { 17 | if (i % 5 == 0) { // i.e., detect an error 18 | throw new RuntimeException("Interesting data"); 19 | } 20 | } 21 | 22 | /** 23 | * This is a bad example of the above, because it will fail silently if 24 | * called from a GUI or from a Servlet - let the calling code display 25 | * errors to the user, it's the only place that knows how! 26 | */ 27 | void badExampleOfLibraryCode(int i) throws Exception { 28 | if (i % 5 == 0) { // i.e., detect an error 29 | System.out.println("Interesting data"); 30 | } 31 | } 32 | 33 | /** How not to do it. */ 34 | void antiPattern() { 35 | try { 36 | for (int i = 0; i < 10; i++) { 37 | exampleOfLibraryCode(i); 38 | } 39 | } catch (Exception e) { 40 | System.out.println("Error: " + e); 41 | } 42 | } 43 | 44 | /** A better way. */ 45 | void pattern() { 46 | for (int i = 0; i < 10; i++) { 47 | try { 48 | exampleOfLibraryCode(i); 49 | } catch (Exception e) { 50 | System.out.println("Error: " + e); 51 | } 52 | } 53 | } 54 | 55 | public static void main(String[] args) { 56 | System.out.println("Demo"); 57 | new ErrorHandling().pattern(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/coding/README.txt: -------------------------------------------------------------------------------- 1 | These are "patterns" that are coding patterns, not really design patterns. 2 | -------------------------------------------------------------------------------- /src/main/java/creation/AbstractFactoryDemo.java: -------------------------------------------------------------------------------- 1 | package creation; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.util.Properties; 6 | 7 | /** 8 | * A really really simple example of a configurable AbstractFactory 9 | * This Abstract Factory gives out DAO's whose type is 10 | * determined from the properties file, and which would 11 | * (in a fuller example) in turn give out, say, 12 | * JPAMusicDao, JPAVideoDao, JPABookDao, etc., when the 13 | * configuration was set to JPA. 14 | *
15 | * Reads from a Properties file on classpath of the form 16 | * dao_category= one of the FactoryType 17 | * e.g.: 18 | * dao_category=JPA 19 | */ 20 | public class AbstractFactoryDemo { 21 | 22 | // This "file name" is used to load the props from classpath 23 | // Since it's in this same directory, the path is the 24 | // full package name follwed by the filename. 25 | private static final String FACTORY_CONFIG_RESOURCE_NAME = 26 | "/patterns/creation/factory.config"; 27 | 28 | /** Enumerate all the supported DAO technologies */ 29 | enum FactoryTechnology { 30 | JDBC, 31 | JPA, 32 | HIBERNATE 33 | } 34 | static FactoryTechnology tech; 35 | 36 | public static void main(String[] args) throws Exception { 37 | // Get the string name of the FactoryTechnology to use 38 | final String property = props.getProperty("dao_type"); 39 | // Get the enum that goes with it 40 | tech = FactoryTechnology.valueOf(property); 41 | // Get the DaoFactory of the configured type 42 | final DaoFactory daoFactory = getDaoFactory(); 43 | // Pick one (the only one implemented so far) DAO 44 | final Object musicDao = daoFactory.getMusicDao(); 45 | // Print it out for verification. 46 | System.out.println("Factory " + daoFactory + " gave us " + musicDao); 47 | } 48 | 49 | /** 50 | * The Abstract Factory Method: gives the concrete factory. 51 | * @return The configured DaoFactory. 52 | */ 53 | public static DaoFactory getDaoFactory() { 54 | switch(tech) { 55 | case JDBC: return new JdbcConnectionFactory(); 56 | case JPA: return new JpaConnectionFactory(); // actually returns EntityManager 57 | case HIBERNATE: return new HibernateConnectionFactory(); // returns HibernateSession 58 | default: 59 | throw new IllegalArgumentException("Unknown Factory Type"); 60 | } 61 | } 62 | 63 | /** Dummy definition of the DaoFactory, just enough 64 | * to make this compile and sort of work. 65 | * The return value is Object, in reality it would 66 | * be something like MusicDao, but I wanted to keep 67 | * this example short. 68 | */ 69 | interface DaoFactory { 70 | Object getMusicDao(); 71 | // Object getVideoDao(); 72 | // Object getBookDao(); 73 | } 74 | 75 | // The various concrete Factory classes. 76 | static class JdbcConnectionFactory implements DaoFactory { 77 | public Object getMusicDao() { 78 | return "My Dummy JDBC Music Dao"; 79 | } 80 | } 81 | static class HibernateConnectionFactory implements DaoFactory { 82 | public Object getMusicDao() { 83 | return "My Fake Hibernate Music Dao"; 84 | } 85 | } 86 | static class JpaConnectionFactory implements DaoFactory { 87 | public Object getMusicDao() { 88 | return "Trust me! This is a JPA Music Dao"; 89 | } 90 | } 91 | 92 | // Stuff for loading properties 93 | static Properties props = new Properties(); 94 | 95 | // Static initializer to load the properties file. 96 | static { 97 | try { 98 | InputStream stream = 99 | AbstractFactoryDemo.class.getResourceAsStream(FACTORY_CONFIG_RESOURCE_NAME); 100 | if (stream == null) { 101 | throw new ExceptionInInitializerError("Can't load properties file from classpath: " + FACTORY_CONFIG_RESOURCE_NAME); 102 | } 103 | props.load(stream); 104 | } catch (IOException e) { 105 | throw new ExceptionInInitializerError(e); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/creation/FactoryDemo.java: -------------------------------------------------------------------------------- 1 | package creation; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.util.Properties; 6 | 7 | /** 8 | * A really really simple example of a configurable Factory. 9 | * This factory does Inversion of Control but NOT 10 | * Dependency Injection; for the full treatment, 11 | * use JavaEE-CDI or use Spring. This simple version 12 | * reads from a Properties file on classpath of the form 13 | * beanName=fullClassName 14 | * e.g.: 15 | * renderer=patterns.creation.MyRenderer 16 | */ 17 | public class FactoryDemo { 18 | 19 | private static final String FACTORY_CONFIG_RESOURCE_NAME = 20 | "/creation/factory.config"; 21 | 22 | public static void main(String[] args) throws Exception { 23 | MessageRenderer r = 24 | FactoryDemo.getBean("renderer", MessageRenderer.class); 25 | System.out.println("Renderer is of type " + r.getClass().getName()); 26 | r.renderMessage("Hello from the main program"); 27 | } 28 | 29 | static Properties props = new Properties(); 30 | 31 | static { 32 | try { 33 | InputStream stream = 34 | FactoryDemo.class.getResourceAsStream(FACTORY_CONFIG_RESOURCE_NAME); 35 | if (stream == null) { 36 | throw new ExceptionInInitializerError("Can't load properties file from classpath: " + FACTORY_CONFIG_RESOURCE_NAME); 37 | } 38 | props.load(stream); 39 | } catch (IOException e) { 40 | throw new ExceptionInInitializerError(e); 41 | } 42 | } 43 | 44 | /** Construct and return a bean whose declared class is clazz 45 | * and whose implementing class is the value of "bean" in a props file. 46 | * @param name - The name the Bean should have in the props/config 47 | * @param clazz - the declared (often an interface) class 48 | * @return The instantiated bean. 49 | * @throws Exception 50 | */ 51 | public static T getBean(String name, Class clazz) throws Exception { 52 | final String clazzName = props.getProperty(name); 53 | @SuppressWarnings("unchecked") 54 | final Class c = (Class) Class.forName(clazzName); 55 | return c.getConstructor().newInstance(); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/creation/MessageRenderer.java: -------------------------------------------------------------------------------- 1 | package creation; 2 | 3 | /** Dummy interface, just for use in Factory demos */ 4 | public interface MessageRenderer { 5 | void renderMessage(String message); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/creation/MyRenderer.java: -------------------------------------------------------------------------------- 1 | package creation; 2 | 3 | public class MyRenderer implements MessageRenderer { 4 | public void renderMessage(String message) { 5 | System.out.println(message); 6 | } 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/creation/ReloadingFactory.java: -------------------------------------------------------------------------------- 1 | package creation; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.IOException; 6 | import java.net.URL; 7 | import java.util.Properties; 8 | 9 | /** A Factory class as per page 517-2-20 that 10 | * does not need reloading when the file changes. 11 | * @author Ian Darwin, http://darwinsys.com/ 12 | * 13 | */ 14 | public class ReloadingFactory { 15 | private final static Properties p = new Properties(); 16 | private final static String CONFIG_FILE = "patterns/creation/factory.config"; 17 | private final static URL fileLoc = ReloadingFactory.class.getClassLoader().getResource(CONFIG_FILE); 18 | private static File f = new File(fileLoc.getFile()); // contains full path to file! 19 | private static long timestamp = -1; 20 | 21 | public static void main(String[] args) throws Exception { 22 | MessageRenderer p = ReloadingFactory.getBean("renderer", MessageRenderer.class); 23 | System.out.println("Factory gave us a " + p.getClass() + " instance"); 24 | System.out.println("You now have 15sec to edit " + f.getAbsolutePath()); 25 | Thread.sleep(15 * 1000); 26 | MessageRenderer p2 = ReloadingFactory.getBean("renderer", MessageRenderer.class); 27 | System.out.println("Factory gave us a " + p2.getClass() + " instance"); 28 | } 29 | 30 | /** Get a particular kind of bean, the MessageRenderer instance */ 31 | public static MessageRenderer getMessageRenderer() { 32 | try { 33 | return (MessageRenderer) 34 | Class.forName(getConfigProperty("renderer")).getConstructor().newInstance(); 35 | } catch (Exception e) { 36 | throw new RuntimeException("Cant load Renderer " + e, e); 37 | } 38 | } 39 | 40 | /** Generic getBean(name, TypeParameter) a la Spring 3.x */ 41 | @SuppressWarnings("unchecked") 42 | public static T getBean(String name, Class T) { 43 | try { 44 | final String clazzName = getConfigProperty(name); 45 | if (clazzName == null) { 46 | throw new RuntimeException("Config property " + name + " not found."); 47 | } 48 | return (T) Class.forName(clazzName).getConstructor().newInstance(); 49 | } catch (Exception e) { 50 | throw new RuntimeException("Can't load Renderer " + e, e); 51 | } 52 | } 53 | 54 | /** Read a property from the Config file, (re)loading it if necessary */ 55 | private static synchronized String getConfigProperty(String name) throws IOException { 56 | if (f.lastModified() != timestamp) { 57 | reload(); 58 | timestamp = f.lastModified(); 59 | } 60 | return p.getProperty(name); 61 | } 62 | 63 | /** Lazily (re)load the config file */ 64 | private static synchronized void reload() throws IOException { 65 | FileInputStream is = null; 66 | try { 67 | is = new FileInputStream(f); 68 | p.load(is); 69 | } finally { 70 | if (is != null) { 71 | is.close(); 72 | } 73 | } 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/creation/Singleton.java: -------------------------------------------------------------------------------- 1 | package creation; 2 | 3 | /** An example of a Singleton implementation in Java, using static initialization. 4 | * The Singleton design pattern is described in GOF; the idea is to ensure 5 | * that only one instance of the class will exist in a given application. 6 | * @author Ian F. Darwin, https://darwinsys.com/ 7 | */ 8 | // tag::main[] 9 | public class Singleton { 10 | 11 | /** 12 | * Static Initializer is run before class is available to code, avoiding 13 | * broken anti-pattern of lazy initialization in instance method. 14 | * For more complicated construction, could use static block initializer. 15 | */ 16 | private static Singleton instance = new Singleton(); 17 | 18 | /** A private Constructor prevents any other class from instantiating. */ 19 | private Singleton() { 20 | // nothing to do this time 21 | } 22 | 23 | /** Static 'instance' method */ 24 | public static Singleton getInstance() { 25 | return instance; 26 | } 27 | 28 | // other methods protected by singleton-ness would be here... 29 | 30 | /** A simple demo method */ 31 | public String demoMethod() { 32 | return "demo"; 33 | } 34 | } 35 | // end::main[] 36 | -------------------------------------------------------------------------------- /src/main/java/creation/SingletonDemo.java: -------------------------------------------------------------------------------- 1 | package creation; 2 | 3 | public class SingletonDemo { 4 | public static void main(String[] args) { 5 | 6 | // tag::enumBased[] 7 | // Demonstrate the enum method: 8 | SingletonEnum.INSTANCE.demoMethod(); 9 | // end::enumBased[] 10 | 11 | // tag::codeBased[] 12 | // Demonstrate the codeBased method: 13 | Singleton.getInstance().demoMethod(); 14 | // end::codeBased[] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/creation/SingletonEnum.java: -------------------------------------------------------------------------------- 1 | package creation; 2 | 3 | /** 4 | * Create a singleton by creative re-use (or even abuse) 5 | * of the Java 5 Enum mechanism. The JVM tries to guarantee 6 | * the singleness of each Enum constant. 7 | * @author Ian Darwin 8 | */ 9 | public enum SingletonEnum { 10 | INSTANCE; 11 | 12 | /** A simple demo method */ 13 | public String demoMethod() { 14 | return "demo"; 15 | } 16 | 17 | // other methods protected by singleton-ness would be here... 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/creation/SingletonLazy.java: -------------------------------------------------------------------------------- 1 | package creation; 2 | 3 | /** An example of a Singleton implementation in Java, using lazy initialization. 4 | * The Singleton design pattern is described in GOF; the idea is to ensure 5 | * that only one instance of the class will exist in a given application. 6 | * @author Ian F. Darwin, https://darwinsys.com/ 7 | */ 8 | public class SingletonLazy { 9 | 10 | private static SingletonLazy instance; 11 | 12 | /** A private Constructor prevents any other class from instantiating. */ 13 | private SingletonLazy() { 14 | // nothing to do this time 15 | } 16 | 17 | /** Static 'instance' method, replete with lazy construction. Having it 18 | * all synchronized is the best way to make it thread safe; see the older paper 19 | * "The "Double-Checked Locking is Broken" Declaration" signed by the likes 20 | * of Josh Bloch, Doug Lea, Bill Pugh, and others, online at 21 | * http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html 22 | */ 23 | public synchronized static SingletonLazy getInstance() { 24 | if (instance == null) { 25 | instance = new SingletonLazy(); 26 | } 27 | return instance; 28 | } 29 | 30 | /** A simple demo method */ 31 | public String demoMethod() { 32 | return "demo"; 33 | } 34 | 35 | // other methods protected by singleton-ness would be here... 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/creation/SwingRenderer.java: -------------------------------------------------------------------------------- 1 | package creation; 2 | 3 | import javax.swing.JOptionPane; 4 | 5 | public class SwingRenderer implements MessageRenderer { 6 | public void renderMessage(String message) { 7 | JOptionPane.showMessageDialog(null, message); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/creation/builder/Play.java: -------------------------------------------------------------------------------- 1 | package creation.builder; 2 | 3 | public class Play { 4 | int id; 5 | String job; 6 | String firstName; 7 | String lastName; 8 | 9 | /** This is the main constructor, used from the Builder */ 10 | private Play(PlayBuilder b) { 11 | this.id = b.id; 12 | this.job = b.job; 13 | this.firstName = b.firstName; 14 | this.lastName = b.lastName; 15 | } 16 | 17 | /** 18 | * This constructor is left here, non-public, just to show the "old way"; 19 | * in real life it should be removed, or marked private and called only 20 | * from the Builder.build() method 21 | */ 22 | Play(int id, 23 | String job, 24 | String firstName, 25 | String lastName) { 26 | this.id = id; 27 | this.job = job; 28 | this.firstName = firstName; 29 | this.lastName = lastName; 30 | } 31 | 32 | /** The builder is here as a nested class so it can use the private constructor */ 33 | public static class PlayBuilder { 34 | private int id; 35 | private String job; 36 | private String firstName; 37 | private String lastName; 38 | 39 | private PlayBuilder() { 40 | // We control construction 41 | } 42 | 43 | public static PlayBuilder builder() { 44 | return new PlayBuilder(); 45 | } 46 | 47 | public PlayBuilder id(int id) { 48 | this.id = id; 49 | return this; 50 | } 51 | 52 | public PlayBuilder job(String job) { 53 | this.job = job; 54 | return this; 55 | } 56 | 57 | public PlayBuilder firstName(String firstName) { 58 | this.firstName = firstName; 59 | return this; 60 | } 61 | 62 | public PlayBuilder lastName(String lastName) { 63 | this.lastName = lastName; 64 | return this; 65 | } 66 | 67 | public Play build() { 68 | return new Play(this); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/creation/builder/PlayDemo.java: -------------------------------------------------------------------------------- 1 | package creation.builder; 2 | 3 | public class PlayDemo { 4 | // Traditional construction - note 3 Strings in a row, easy to get order wrong 5 | Play p1 = new Play(1, "Hoverboardist", "Robin", "Smith"); 6 | 7 | // Builder construction - more verbose, but far less chance of data in wrong field. 8 | Play p2 = Play.PlayBuilder.builder() 9 | .id(1) 10 | .job("Hoverboardist") 11 | .firstName("Robin") 12 | .lastName("Smith").build(); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/creation/builder/README.adoc: -------------------------------------------------------------------------------- 1 | = Builder 2 | 3 | Builder pattern moves the creational interface from traditional constructors 4 | to a new Builder class. The benefit is that, instead of a single method with 5 | a zillion arguments, many of which may be of the same type (int and String 6 | are common), you have a Builder object with setter-like methods to install 7 | the various fields into the builder before the call to build(). 8 | 9 | The build() method also provides a choke point for cross-field validation. 10 | 11 | The Builder may be a top-level class distinct from the built class, in which 12 | case the constructor must remain public (or at least package visible); this 13 | is how IntelliJ's `Replace Constructor With Builder` refactoring does things 14 | when it gets to create the builder. Alternately, as shown here, the Builder 15 | can be an inner class of the built class, in which case the Builder methods 16 | are public and the constructor(s) become private; IRL you only need the 17 | constructor that accepts the Builder as an argument. 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/main/java/structure/adapter/AdapterDemo.java: -------------------------------------------------------------------------------- 1 | package structure.adapter; 2 | 3 | public class AdapterDemo { 4 | public static void main(String[] args) { 5 | LibAdapter lib = LibAdapter.getAdapter(); 6 | lib.process(123, "Ian"); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/structure/adapter/Lib1.java: -------------------------------------------------------------------------------- 1 | package structure.adapter; 2 | 3 | public class Lib1 { 4 | 5 | public void doSomething(String name, int number) { 6 | System.out.printf("Lib ONE got %s, %d\n", name, number); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/structure/adapter/Lib1Adapter.java: -------------------------------------------------------------------------------- 1 | package structure.adapter; 2 | 3 | public class Lib1Adapter implements LibAdapter { 4 | 5 | Lib1 lib = new Lib1(); 6 | 7 | public void process(int number, String name) { 8 | lib.doSomething(name, number); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/structure/adapter/Lib2.java: -------------------------------------------------------------------------------- 1 | package structure.adapter; 2 | 3 | public class Lib2 { 4 | 5 | public void handleData(int number, String name) { 6 | System.out.printf("Lib TWO got %s, %d\n", name, number); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/structure/adapter/Lib2Adapter.java: -------------------------------------------------------------------------------- 1 | package structure.adapter; 2 | 3 | public class Lib2Adapter implements LibAdapter { 4 | 5 | Lib2 lib = new Lib2(); 6 | 7 | public void process(int number, String name) { 8 | lib.handleData(number, name); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/structure/adapter/LibAdapter.java: -------------------------------------------------------------------------------- 1 | package structure.adapter; 2 | 3 | public interface LibAdapter { 4 | 5 | public void process(int num, String name); 6 | 7 | static LibAdapter getAdapter() { 8 | if (Math.random() > 0.5) { 9 | return new Lib1Adapter(); 10 | } else { 11 | return new Lib2Adapter(); 12 | } 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/structure/adapter/SimpleAdapter.java: -------------------------------------------------------------------------------- 1 | package structure.adapter; 2 | 3 | public interface SimpleAdapter { 4 | 5 | public void process(int num, String name); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/structure/decorator/DigitalImage.java: -------------------------------------------------------------------------------- 1 | package structure.decorator; 2 | 3 | /** 4 | * A DigitalImage is one PhotoImage on sale at one stock agency 5 | */ 6 | public class DigitalImage extends ImageDecorator { 7 | StockAgency agency; 8 | boolean exclusive; 9 | double sellingPrice; 10 | 11 | public DigitalImage(PhotoImage target, StockAgency agency, double sellingPrice) { 12 | super(target); 13 | this.agency = agency; 14 | this.sellingPrice = sellingPrice; 15 | } 16 | 17 | public String getDescription() { 18 | return target.getDescription() + ", Digital Rights via " + agency; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/structure/decorator/Frame.java: -------------------------------------------------------------------------------- 1 | package structure.decorator; 2 | 3 | public class Frame extends ImageDecorator { 4 | 5 | public Frame(PhotoImage target) { 6 | super(target); 7 | } 8 | 9 | @Override 10 | public String getDescription() { 11 | return target.getDescription() + ", Framed"; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/structure/decorator/IOStreamsDemo.java: -------------------------------------------------------------------------------- 1 | package structure.decorator; 2 | 3 | import java.io.*; 4 | 5 | public class IOStreamsDemo { 6 | class FileProvider { File getFile() { return new File("README.adoc"); }} 7 | FileProvider foo; 8 | 9 | void demo() throws Exception { 10 | try ( 11 | BufferedReader is = new BufferedReader(new FileReader("some filename here")); 12 | PrintWriter pout = new PrintWriter(new FileWriter("output filename here")); 13 | LineNumberReader lrdr = new LineNumberReader(new FileReader(foo.getFile()))) { 14 | // empty - wrapped in try(){} to silence leak warnings. 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/structure/decorator/ImageDecorator.java: -------------------------------------------------------------------------------- 1 | package structure.decorator; 2 | 3 | public abstract class ImageDecorator extends PhotoImage { 4 | protected PhotoImage target; 5 | 6 | public ImageDecorator(PhotoImage target) { 7 | this.target = target; 8 | } 9 | 10 | @Override 11 | public String getDescription() { 12 | return target.getDescription(); 13 | } 14 | 15 | @Override 16 | public String toString() { 17 | return getDescription(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/structure/decorator/ImageSales.java: -------------------------------------------------------------------------------- 1 | package structure.decorator; 2 | 3 | public class ImageSales { 4 | 5 | /** 6 | * Create and print some images in various degrees of decoratedness. 7 | * IRL this code would probably be in a Factory pattern implementation. 8 | */ 9 | public static void main(String[] args) { 10 | // Create an un-decorated image 11 | final PhotoImage image = new PhotoImage("Sunset at Tres Ríos", "2020/ifd12345.jpg"); 12 | 13 | // Make a print of that, on usletter paper 14 | Print im1 = new Print(11, 8.5, image); 15 | addToPrintOrder(im1); 16 | 17 | // Make a 19x11 print of a second image, matted in green, framed. 18 | Print im2 = 19 | new Print(19, 11, 20 | new Frame( 21 | new Mat("Lime Green", 22 | new PhotoImage("Goodbye at the Station", "1968/ifd.00042.jpg")))); 23 | addToPrintOrder(im2); 24 | 25 | // Make a digital print sale 26 | PhotoImage dig = new DigitalImage(image, StockAgency.Getty, 135.00); 27 | System.out.println(dig); 28 | } 29 | 30 | private static void addToPrintOrder(Print image) { 31 | System.out.println(image); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/structure/decorator/Mat.java: -------------------------------------------------------------------------------- 1 | package structure.decorator; 2 | 3 | public class Mat extends ImageDecorator { 4 | String color; 5 | 6 | public Mat(String color, PhotoImage target) { 7 | super(target); 8 | this.color = color; 9 | } 10 | 11 | @Override 12 | public String getDescription() { 13 | return target.getDescription() + ", Matted(" + color + ")"; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/structure/decorator/PhotoImage.java: -------------------------------------------------------------------------------- 1 | package structure.decorator; 2 | 3 | /** 4 | * A PhotoImage is a picture that I took at some point. 5 | */ 6 | public class PhotoImage { 7 | /** The human-readable title */ 8 | String title; 9 | /** Where the actual pixels are */ 10 | String fileName; 11 | /** How many pixels there are in the image file */ 12 | int pixWidth, pixHeight; 13 | 14 | public PhotoImage() { 15 | // Empty; used in Decorators. 16 | } 17 | 18 | public PhotoImage(String title, String fileName) { 19 | super(); 20 | this.title = title; 21 | this.fileName = fileName; 22 | } 23 | 24 | /** 25 | * Get a printable description; may be more detailed than toString() 26 | * but at any rate is the example delegation method. 27 | */ 28 | public String getDescription() { 29 | return getTitle(); 30 | } 31 | 32 | /** Default toString() just uses getDescription */ 33 | @Override 34 | public String toString() { 35 | return getDescription(); 36 | } 37 | 38 | public String getTitle() { 39 | return title; 40 | } 41 | 42 | public void setTitle(String title) { 43 | this.title = title; 44 | } 45 | 46 | public String getFileName() { 47 | return fileName; 48 | } 49 | 50 | public void setFileName(String fileName) { 51 | this.fileName = fileName; 52 | } 53 | 54 | public int getPixWidth() { 55 | return pixWidth; 56 | } 57 | 58 | public void setPixWidth(int width) { 59 | this.pixWidth = width; 60 | } 61 | 62 | public int getPixHeight() { 63 | return pixHeight; 64 | } 65 | 66 | public void setPixHeight(int height) { 67 | this.pixHeight = height; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/structure/decorator/Print.java: -------------------------------------------------------------------------------- 1 | package structure.decorator; 2 | 3 | /** 4 | * A Print represents a PhotoImage with a physical size to print it at. 5 | */ 6 | public class Print extends ImageDecorator { 7 | /** PrintWidth, PrintHeight are in inches for our US audience */ 8 | private double printWidth, printHeight; 9 | 10 | public Print(double printWidth, double printHeight, PhotoImage target) { 11 | super(target); 12 | this.printWidth = printWidth; 13 | this.printHeight = printHeight; 14 | } 15 | 16 | @Override 17 | public String getDescription() { 18 | return target.getDescription() + " " + String.format("(%4.1f x %4.1f in)", getPrintWidth(), getPrintHeight()); 19 | } 20 | 21 | public double getPrintWidth() { 22 | return printWidth; 23 | } 24 | 25 | public void setPrintWidth(double printWidth) { 26 | this.printWidth = printWidth; 27 | } 28 | 29 | public double getPrintHeight() { 30 | return printHeight; 31 | } 32 | 33 | public void setPrintHeight(double printHeight) { 34 | this.printHeight = printHeight; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/structure/decorator/StockAgency.java: -------------------------------------------------------------------------------- 1 | package structure.decorator; 2 | 3 | enum StockAgency {Adobe, DreamsTime, Five00px, Getty, Shutterstock } -------------------------------------------------------------------------------- /src/main/java/structure/decorator/StreamDemo.java: -------------------------------------------------------------------------------- 1 | package structure.decorator; 2 | 3 | import java.util.regex.Pattern; 4 | import java.util.stream.Stream; 5 | 6 | public class StreamDemo { 7 | 8 | static Pattern regex = Pattern.compile("^[A-Z]"); 9 | 10 | public static void main(String[] args) { 11 | Stream.of("Red", "Yellow", "Blue", "book") 12 | .filter(regex.asPredicate()) 13 | .sorted() 14 | .forEach(System.out::println); 15 | 16 | // The boring, verbose way. 17 | Stream tempStream1 = Stream.of("Red", "Yellow", "Blue", "book"); 18 | Stream tempStream2 = tempStream1.filter(regex.asPredicate()); 19 | Stream tempStream3 = tempStream2.sorted(); 20 | tempStream3.forEach(System.out::println); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/structure/decorator/TransactionInterceptorPseudoCode.java: -------------------------------------------------------------------------------- 1 | package structure.decorator; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | public class TransactionInterceptorPseudoCode { 6 | 7 | UserTransaction transaction; // Injected by the container 8 | 9 | // PSEUDO-CODE 10 | public void interceptTransaction(Method method, Object target, Object[] args) throws Exception { 11 | if (transaction.getStatus() != Status.NoTransaction) { 12 | transaction.begin(); 13 | } 14 | try { 15 | method.invoke(target, args); 16 | transaction.commit(); 17 | } catch (Exception ex) { 18 | transaction.rollback(); 19 | throw ex; 20 | } 21 | } 22 | 23 | 24 | // Completely bogus, just to make the pseudo-code compile 25 | // Avoids depending on Java EE API just for this little bit. 26 | class UserTransaction { 27 | boolean exists() { return false; } 28 | void join() {} 29 | void begin() {} 30 | void commit() {} 31 | void rollback() {} 32 | int getStatus() { return 0; } 33 | } 34 | class Status { 35 | final static int NoTransaction = 0; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/structure/proxy/CdiDemoSe.java: -------------------------------------------------------------------------------- 1 | package structure.proxy; 2 | 3 | import javax.enterprise.inject.Instance; 4 | import javax.enterprise.inject.Produces; 5 | import javax.enterprise.inject.se.SeContainer; 6 | import javax.enterprise.inject.se.SeContainerInitializer; 7 | 8 | public class CdiDemoSe { 9 | 10 | public static void main(String[] args) { 11 | 12 | SeContainerInitializer initializer = SeContainerInitializer.newInstance(); 13 | 14 | try (SeContainer container = initializer.initialize()) { 15 | final Instance selected = container.select(QuoteServer.class); 16 | // selected.forEach(System.out::println); 17 | QuoteServer qs = selected.get(); 18 | System.out.println(qs.getQuote()); 19 | } 20 | } 21 | @Produces 22 | public QuoteServer getQuoter() { 23 | return new QuoteServerImpl(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/structure/proxy/CdiLoggingInterceptor.java: -------------------------------------------------------------------------------- 1 | package structure.proxy; 2 | 3 | import java.lang.reflect.Field; 4 | import java.lang.reflect.Method; 5 | import java.util.List; 6 | 7 | import javax.interceptor.AroundInvoke; 8 | import javax.interceptor.Interceptor; 9 | import javax.interceptor.InvocationContext; 10 | 11 | /** 12 | * A Logging Interceptor for CDI. 13 | * There's a fancier version using logging with SLF4J 14 | * on GitHub at https://github.com/t1/logging-interceptor 15 | */ 16 | @Interceptor 17 | public class CdiLoggingInterceptor { 18 | 19 | public CdiLoggingInterceptor() { 20 | // System.out.println("LoggingInterceptor.init()"); 21 | } 22 | 23 | // @AroundInvoke applies to business method; see also @AroundTimeout for timeout methods, etc. 24 | @AroundInvoke 25 | public Object log(InvocationContext ctx) throws Throwable { 26 | final Object[] parameters = ctx.getParameters(); 27 | final Method method = ctx.getMethod(); 28 | String firstArg = (parameters.length > 0) ? "First is: " + format(parameters[0]) : "(empty)"; 29 | log(String.format("About to call %s with %d arg(s): %s", 30 | method.getName(), parameters.length, firstArg)); 31 | Object o = ctx.proceed(); // The actual call! 32 | log("Returned " + format(o) + " from method " + method.getName()); 33 | return o; 34 | } 35 | 36 | /** Well, this part is no longer trivial; format the argument list a bit */ 37 | String format(Object o) { 38 | if (o instanceof String) { 39 | return (String)o; 40 | } 41 | if (o instanceof List) { 42 | int size = ((List)o).size(); 43 | return String.format("List(size %d): first is of type %s", size, 44 | size > 0 ? ((List)o).get(0).getClass().getName() : "?"); 45 | } 46 | if (o.getClass().isArray()) { 47 | Integer length = null; 48 | try { 49 | Field field = o.getClass().getField("length"); 50 | length = (Integer)field.get(o); 51 | } catch (NoSuchFieldException | SecurityException | IllegalAccessException e) { 52 | return "Array"; 53 | } 54 | String base = o.getClass().getComponentType().getName(); 55 | return String.format("Array length %d of %s", length, base); 56 | } 57 | return o.toString(); 58 | } 59 | 60 | // Following is how it would normally be used; commented out b/c it can't 61 | // (or at least shouldn't!) be used on itself. 62 | 63 | // @Interceptors({LoggingInterceptor.class,LoggingInterceptor.class}) 64 | public void validateCredit() { 65 | System.out.println("LoggingInterceptor.validateCredit(): I was here"); 66 | } 67 | 68 | void log(String mesg) { 69 | System.out.println(mesg); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/structure/proxy/DynamicProxyDemo.java: -------------------------------------------------------------------------------- 1 | package structure.proxy; 2 | 3 | import java.lang.reflect.InvocationHandler; 4 | import java.lang.reflect.Proxy; 5 | import java.lang.reflect.Method; 6 | 7 | /** 8 | * Simple demo of Java SE's "dynamic proxy" mechanism. 9 | * Dynamic proxies allow you to create an implementation of 10 | * an interface without having to know the class name, 11 | * providing more flexibility. 12 | * @author Ian Darwin 13 | */ 14 | public class DynamicProxyDemo { 15 | 16 | /** Here we show the whole thing in operation. */ 17 | public static void main(String[] args) { 18 | 19 | // Proxy is commonly used with some kind of creational method which 20 | // the client calls to obtain the "real" object but actually gets 21 | // the proxied object, the proxy must therefore be substituable 22 | // for the real object 23 | QuoteServer quoteServer = getQuoteServer(); 24 | 25 | System.out.println("QuoteServer object is " + quoteServer.getClass().getName()); 26 | quoteServer.addQuote("Only the educated are free -- Epictetus"); 27 | System.out.println("QuoteServer returned: " + quoteServer.getQuote()); 28 | } 29 | 30 | public static QuoteServer getQuoteServer() { 31 | QuoteServer target = new QuoteServerImpl(); 32 | InvocationHandler handler = new MyInvocationHandler(target); 33 | return (QuoteServer) Proxy.newProxyInstance( 34 | QuoteServer.class.getClassLoader(), 35 | new Class[] { QuoteServer.class }, handler); 36 | } 37 | 38 | /** The InvocationHandler, called whenever any method on the proxy is invoked. 39 | * Note that the invoke() method only needs a reference to the implementation 40 | * object; it does not (necessarily) need to figure out which method to call, 41 | * because it's passed a Method descriptor. 42 | */ 43 | static class MyInvocationHandler implements InvocationHandler { 44 | 45 | private Object target; 46 | 47 | public MyInvocationHandler(Object target) { 48 | super(); 49 | this.target = target; 50 | } 51 | 52 | /** 53 | * Method that is called for every call into the proxy; 54 | * this has to invoke the method on the real object. 55 | * This method demonstrates both logging and security checking. 56 | */ 57 | public Object invoke(Object proxyObject, Method method, Object[] argList) 58 | throws Throwable { 59 | String name = method.getName() + "()"; 60 | System.out.println("Proxy got request for " + name); // or your favorite logging package 61 | final String userName = System.getProperty("user.name"); 62 | if (name.startsWith("add") && !userName.equals("ian")) 63 | throw new SecurityException("User " + userName + " not allowed to add quotes."); 64 | Object ret = method.invoke(target, argList); 65 | System.out.println("Proxy returned from " + name); 66 | return ret; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/structure/proxy/QuoteServer.java: -------------------------------------------------------------------------------- 1 | package structure.proxy; 2 | 3 | /** The interface that the impl and the proxy both implement. */ 4 | public interface QuoteServer { 5 | /** Serve up a randomly-chosen quote */ 6 | public String getQuote(); 7 | /** Admin tool to add quotes */ 8 | public void addQuote(String newQuote); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/main/java/structure/proxy/QuoteServerImpl.java: -------------------------------------------------------------------------------- 1 | package structure.proxy; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** Our private implementation of the interface */ 7 | public class QuoteServerImpl implements QuoteServer { 8 | 9 | final List sayings = new ArrayList(); 10 | 11 | QuoteServerImpl() { 12 | sayings.add("A stitch in time... is better late than never"); 13 | sayings.add("JavaScript is to Java as George Burns is to George Washington."); 14 | sayings.add("The more old you get, the more you forget"); 15 | sayings.add("Always in motion, the future is. -- Yoda"); 16 | } 17 | 18 | /** Serve up a randomly-chosen quote */ 19 | public String getQuote() { 20 | // Get an int in 0..list.size()-1, get that saying from the list. 21 | return (String)sayings.get((int)(Math.random()*sayings.size())); 22 | } 23 | 24 | public void addQuote(String newQuote) { 25 | sayings.add(newQuote); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/structure/proxy/ShoppingService.java: -------------------------------------------------------------------------------- 1 | package structure.proxy; 2 | 3 | import javax.interceptor.Interceptors; 4 | 5 | /** Simple non-working example of Transaction annotations */ 6 | public class ShoppingService { 7 | private ShoppingCart cart; 8 | private Dao dao; 9 | 10 | @Transactional(TransactionType.REQUIRED) 11 | public void addToCart(Product p) { 12 | // do validation/calculation work here 13 | dao.saveCart(cart); 14 | } 15 | 16 | @Interceptors({CdiLoggingInterceptor.class}) 17 | public void validateCredit() { 18 | // do some work here 19 | } 20 | 21 | // Stuff below here is totally bogus, just to make 22 | // the above example compile. 23 | enum TransactionType { REQUIRED, REQUIRES_NEW } 24 | @interface Transactional { 25 | TransactionType value(); 26 | } 27 | class ShoppingCart { 28 | // empty 29 | } 30 | class Product { 31 | // empty 32 | } 33 | class Dao { 34 | void saveCart(ShoppingCart cart) { 35 | // empty 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/structure/proxy/SimpleProxyDemo.java: -------------------------------------------------------------------------------- 1 | package structure.proxy; 2 | 3 | /** 4 | * Simple demo of a manually-built proxy 5 | * @author Ian Darwin 6 | */ 7 | public class SimpleProxyDemo { 8 | final static boolean useProxy = true; 9 | 10 | /** Simple Hand-coded Proxy in operation. */ 11 | public static void main(String[] args) { 12 | 13 | // Proxy is commonly used with some kind of creational method which 14 | // the client calls to obtain the "real" object but actually gets 15 | // the proxied object, the proxy must therefore be substituable 16 | // for the real object 17 | QuoteServer quoteServer = getQuoteServer(); 18 | System.out.println("QuoteServer object is " + quoteServer.getClass().getName()); 19 | quoteServer.addQuote("Only the educated are free -- Epictetus"); 20 | System.out.println("The quote of the day is: " + quoteServer.getQuote()); 21 | } 22 | 23 | public static QuoteServer getQuoteServer() { 24 | final QuoteServer target = new QuoteServerImpl(); 25 | if (!useProxy) { 26 | return target; 27 | } 28 | QuoteServer proxy = new QuoteServer() { 29 | public String getQuote() { 30 | System.out.println("Calling getQuote()"); 31 | return target.getQuote(); 32 | } 33 | public void addQuote(String newQuote) { 34 | // Could put security checking here 35 | System.out.println("Calling addQuote()"); 36 | target.addQuote(newQuote); 37 | } 38 | }; 39 | return proxy; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/structure/proxy/interceptors.adoc: -------------------------------------------------------------------------------- 1 | = CDI Interceptors 2 | 3 | The CDI mechanism provides support for this form of proxying as `Interceptors` 4 | that can be applied to enterprise components via annotations (usually) or by XML configuration. 5 | These are sometimes called decorators because you ask for them, but there is no requirement 6 | that you ask for a specific class. 7 | 8 | Here is how a CDI implementation of our logging interceptor might be used, in a business method 9 | (the curly braces around the class descriptor remind us that it's an array, 10 | in case you want to apply multiple interceptors to the same method). 11 | This annotation can also be applied at class level. 12 | 13 | ---- 14 | @Interceptors({CdiLoggingInterceptor.class}) 15 | public void validateCredit() { 16 | // do some work here 17 | } 18 | ---- 19 | 20 | Here is the code for the logging interceptor, or proxy: 21 | 22 | ---- 23 | import javax.interceptor.AroundInvoke; 24 | import javax.interceptor.Interceptor; 25 | import javax.interceptor.InvocationContext; 26 | 27 | /** 28 | * A Logging Interceptor for CDI. 29 | */ 30 | @Interceptor 31 | public class CdiLoggingInterceptor { 32 | 33 | // @AroundInvoke applies to business method; there are 34 | // also annotations for constructors, timeouts, etc. 35 | @AroundInvoke 36 | public Object log(InvocationContext ctx) throws Throwable { 37 | Object[] parameters = ctx.getParameters(); 38 | String firstArg = (parameters.length > 0) ? 39 | "First is: " + formatArg(parameters[0]) : "(empty)"; 40 | String methodName = ctx.getMethod().getName(); 41 | log(String.format("About to call %s with %d arg(s): %s", 42 | methodName, parameters.length, firstArg)); 43 | Object o = ctx.proceed(); // The actual call! 44 | log("Returned " + format(o) + " from method " + methodName); 45 | return o; 46 | } 47 | ... 48 | } 49 | ---- 50 | 51 | A single parameter, an `InvocationContext`, is passed 52 | which contains the method descriptor, the arguments, etc. 53 | This has a `getMethod()` call which returns the standard Method descriptor, and 54 | a `getParameters()` call which gives you the argument list if you want to examine or modify it. 55 | The `format()` and `log()` methods aren't shown here but are in the online source. 56 | The context `proceed()` method takes the place of the "invoke" method. 57 | 58 | One might think this is a Decorator rather than a Proxy because we are naming the 59 | implementation class. 60 | However, while the `@Interceptors` specifies an exact class, 61 | CDI does allow you to use custom annotations 62 | and resolve the implementation class at runtime using qualifier annotations; 63 | I've not given a full example to keep the code size manageable. 64 | The reader is referred to 65 | https://docs.oracle.com/javaee/7/api/javax/interceptor/package-summary.html#package.description 66 | for more details on the `javax.interceptor` package. 67 | and 68 | https://docs.oracle.com/javaee/7/tutorial/cdi-basic.htm 69 | for tutorial information on CDI itself. 70 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/resources/creation/factory.config: -------------------------------------------------------------------------------- 1 | # The Factory code is from another example; the names 2 | # renderer and provider are just there to show that 3 | # you can create an aribrary number of simple POJOs 4 | # as long as all the classes have no-argument constructors 5 | # 6 | renderer=creation.MyRenderer 7 | #renderer=creation.SwingRenderer 8 | # 9 | provider=java.util.Date 10 | # 11 | # This line is only used by the AbstractFactoryDemo class 12 | dao_type=JPA 13 | -------------------------------------------------------------------------------- /src/test/java/behavioral/bidpay/CompositeCommandTest.java: -------------------------------------------------------------------------------- 1 | package behavioral.bidpay; 2 | 3 | import java.util.*; 4 | 5 | import org.junit.*; 6 | import static org.junit.Assert.*; 7 | 8 | public class CompositeCommandTest { 9 | 10 | boolean flag1, flag2; 11 | 12 | class SimpleCommand implements Command { 13 | Command command; 14 | SimpleCommand(Command command) { 15 | this.command = command; 16 | } 17 | public void execute() { 18 | command.execute(); 19 | } 20 | } 21 | 22 | @Test 23 | public void testComposite() { 24 | flag1 = flag2 = false; 25 | Command c1 = new SimpleCommand(()->flag1 = true); 26 | Command c2 = new SimpleCommand(()->flag2 = true); 27 | new CompositeCommand(Arrays.asList(new Command[]{c1,c2})).execute(); 28 | assertTrue(flag1); 29 | assertTrue(flag2); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/behavioral/visitor/WordCountVisitorTest.java: -------------------------------------------------------------------------------- 1 | package behavioral.visitor; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | public class WordCountVisitorTest { 9 | 10 | WordCountVisitor target; 11 | 12 | @Before 13 | public void setUp() throws Exception { 14 | target = new WordCountVisitor(); 15 | } 16 | 17 | @Test 18 | public void testCounter() { 19 | final TextNode textNode = new TextNode(); 20 | textNode.setText("Avoid controversial statements in examples"); 21 | target.visitTextNode(textNode); 22 | assertEquals(5, target.getWordCount()); 23 | } 24 | 25 | @Test 26 | public void testCounterEmptyString() { 27 | final TextNode textNode = new TextNode(); 28 | textNode.setText(""); 29 | target.visitTextNode(textNode); 30 | assertEquals(0, target.getWordCount()); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/structure/proxy/QuoteServerImplTest.java: -------------------------------------------------------------------------------- 1 | package structure.proxy; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | public class QuoteServerImplTest { 9 | 10 | private static final String LOREM = "Lorem ipsem dolor"; 11 | QuoteServer target; 12 | 13 | @Before 14 | public void setUp() throws Exception { 15 | target = new QuoteServerImpl(); 16 | } 17 | 18 | @Test 19 | public void testGetQuote() { 20 | ((QuoteServerImpl)target).sayings.clear(); 21 | target.addQuote(LOREM); 22 | assertEquals(LOREM, target.getQuote()); 23 | } 24 | 25 | @Test(expected=IndexOutOfBoundsException.class) 26 | public void testSmash() { 27 | ((QuoteServerImpl)target).sayings.clear(); 28 | target.getQuote(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/test/resources/.placeholder: -------------------------------------------------------------------------------- 1 | Useless empty file, to ensure Git creates this folder. 2 | --------------------------------------------------------------------------------