├── .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 |
--------------------------------------------------------------------------------