diff --git a/README.md b/README.md
index a1561b4836eb..19ccf64b7863 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,8 @@
-# Design pattern samples in Java.
+
+
+# Design pattern samples in Java
[](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
@@ -7,26 +11,18 @@
src="https://scan.coverity.com/projects/5634/badge.svg"/>
-
+
+
+
+# Table of Contents
- Introduction
- - List of Design Patterns
- - Creational Patterns
- - Structural Patterns
- - Behavioral Patterns
- - Concurrency Patterns
- - Presentation Tier Patterns
- - Business Tier Patterns
- - Architectural Patterns
- - Integration Patterns
- - Idioms
+ - How to contribute
- Frequently Asked Questions
- - How to contribute
- - Versioning
- Credits
- License
-
-## Introduction
+
+# Introduction [↑](#top)
Design patterns are formalized best practices that the programmer can use to
solve common problems when designing an application or system.
@@ -38,119 +34,11 @@ Reusing design patterns helps to prevent subtle issues that can cause major
problems, and it also improves code readability for coders and architects who
are familiar with the patterns.
-## List of Design Patterns [↑](#top)
-### Creational Patterns [↑](#top)
-
-Creational design patterns abstract the instantiation process. They help make a
-system independent of how its objects are created, composed, and represented.
-
-* [Abstract Factory](#abstract-factory)
-* [Builder](#builder)
-* [Factory Method](#factory-method)
-* [Prototype](#prototype)
-* [Property](#property)
-* [Singleton](#singleton)
-* [Step Builder](#step-builder)
-* [Multiton](#multiton)
-* [Object Pool](#object-pool)
-
-### Structural Patterns [↑](#top)
-
-Structural patterns are concerned with how classes and objects are composed to
-form larger structures.
-
-* [Adapter](#adapter)
-* [Bridge](#bridge)
-* [Composite](#composite)
-* [Decorator](#decorator)
-* [Facade](#facade)
-* [Flyweight](#flyweight)
-* [Proxy](#proxy)
-* [Service Locator](#service-locator)
-* [Servant](#servant)
-* [Event Aggregator](#event-aggregator)
-
-### Behavioral Patterns [↑](#top)
-
-Behavioral patterns are concerned with algorithms and the assignment of
-responsibilities between objects.
-
-* [Chain of responsibility](#chain-of-responsibility)
-* [Command](#command)
-* [Interpreter](#interpreter)
-* [Iterator](#iterator)
-* [Mediator](#mediator)
-* [Memento](#memento)
-* [Observer](#observer)
-* [State](#state)
-* [Strategy](#strategy)
-* [Template method](#template-method)
-* [Visitor](#visitor)
-* [Null Object](#null-object)
-* [Intercepting Filter](#intercepting-filter)
-* [Specification](#specification)
-* [Dependency Injection](#dependency-injection)
-
-### Concurrency Patterns [↑](#top)
-
-Concurrency patterns are those types of design patterns that deal with the
-multi-threaded programming paradigm.
-
-* [Double Checked Locking](#double-checked-locking)
-* [Thread Pool](#thread-pool)
-* [Async Method Invocation](#async-method-invocation)
-* [Half-Sync/Half-Async](#half-sync-half-async)
-
-### Presentation Tier Patterns [↑](#top)
-
-Presentation Tier patterns are the top-most level of the application, this is
-concerned with translating tasks and results to something the user can
-understand.
-
-* [Model-View-Controller](#model-view-controller)
-* [Model-View-Presenter](#model-view-presenter)
-* [Flux](#flux)
-* [Front Controller](#front-controller)
-
-### Business Tier Patterns [↑](#top)
-
-* [Business Delegate](#business-delegate)
-
-### Architectural Patterns [↑](#top)
-
-An architectural pattern is a general, reusable solution to a commonly occurring
-problem in software architecture within a given context.
-
-* [Data Access Object](#dao)
-* [Service Layer](#service-layer)
-* [Naked Objects](#naked-objects)
-* [Repository](#repository)
-
-### Integration Patterns [↑](#top)
-
-Integration patterns are concerned with how software applications communicate
-and exchange data.
-
-* [Tolerant Reader](#tolerant-reader)
-
-### Idioms [↑](#top)
-
-A programming idiom is a means of expressing a recurring construct in one or
-more programming languages. Generally speaking, a programming idiom is an
-expression of a simple task, algorithm, or data structure that is not a built-in
-feature in the programming language being used, or, conversely, the use of an
-unusual or notable feature that is built into a programming language. What
-distinguishes idioms from patterns is generally the size, the idioms tend to be
-something small while the patterns are larger.
-
-* [Execute Around](#execute-around)
-* [Poison Pill](#poison-pill)
-* [Callback](#callback)
-* [Lazy Loading](#lazy-loading)
-* [Double Dispatch](#double-dispatch)
-* [Resource Acquisition Is Initialization](#resource-acquisition-is-initialization)
-* [Private Class Data](#private-class-data)
+# How to contribute [↑](#top)
+
+If you are willing to contribute to the project you will find the relevant information in our [developer wiki](https://github.com/iluwatar/java-design-patterns/wiki).
+
# Frequently asked questions [↑](#top)
@@ -211,53 +99,6 @@ blocked waiting for available object from the pool. This is not the case with
Flyweight.
-
-# How to contribute [↑](#top)
-
-**To work on a new pattern** you need to do the following steps:
-
-1. If there is no issue for the new pattern yet, raise new issue. Comment on
- the issue that you are working on it so that others don't start work on the
- same thing.
-2. Fork the repository.
-3. Implement the code changes in your fork. Remember to add sufficient comments
- documenting the implementation. Reference the issue id e.g. #52 in your
- commit messages.
-4. Create a simple class diagram from your example code.
-5. Add description of the pattern in README.md and link to the class diagram.
-6. Create a pull request.
-
-**To work on one of the non-pattern issues** you need to do the following steps:
-
-1. Check that the issue has "help wanted" badge
-2. Comment on the issue that you are working on it
-3. Fork the repository.
-4. Implement the code changes in your fork. Remember to add sufficient comments
- documenting the implementation. Reference the issue id e.g. #52 in your
- commit messages.
-5. Create a pull request.
-
-**For creating/editing UML diagrams** you need [ObjectAid UML Explorer for Eclipse](http://www.objectaid.com/home).
-
-**For inspiration** check out the following sources:
-
-* there is a good list of design patterns at [Wikipedia](http://en.wikipedia.org/wiki/Software_design_pattern)
-* Martin Fowler's [Catalog of Patterns of Enterprise Application Architecture](http://martinfowler.com/eaaCatalog/)
-* [pattern language for microservices](http://microservices.io/patterns/index.html)
-* Microsoft's [Cloud Design Patterns](http://download.microsoft.com/download/B/B/6/BB69622C-AB5D-4D5F-9A12-B81B952C1169/CloudDesignPatternsBook-PDF.pdf)
-
-**Links to patterns applied in real world applications** are welcome. The links
-should be added to the corresponding section of the `README.md`.
-
-
-# Versioning [↑](#top)
-
-Java-design-patterns project uses [semantic versioning](http://semver.org/)
-scheme. However, version numbers in this project do not signify binary releases
-(since we don't make any) but rather milestones achieved on the roadmap. In
-other words, version numbers are used only for project planning sake.
-
-
# Credits [↑](#top)
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
@@ -285,4 +126,4 @@ other words, version numbers are used only for project planning sake.
# License [↑](#top)
-This project is licensed under the terms of the MIT license.
+This project is licensed under the terms of the MIT license.
\ No newline at end of file
diff --git a/active-record/etc/.gitkeep b/active-record/etc/.gitkeep
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/active-record/index.md b/active-record/index.md
new file mode 100644
index 000000000000..b042104c58ce
--- /dev/null
+++ b/active-record/index.md
@@ -0,0 +1,21 @@
+---
+layout: pattern
+title: Active Record
+folder: active-record
+permalink: /patterns/active-record/
+categories: Architectural
+tags: Java
+---
+
+**Intent:** Active record is an object that wraps a row in a database table or view,
+ encapsulates the database access, and adds domain logic on that data. Active Record
+ uses the most obvious approach, putting data access logic in the domain object.
+
+**Applicability:** Use active record pattern when
+
+* objects correspond directly to the database tables
+* business logic is not too complex
+
+**Real world examples:**
+
+* [ActiveJDBC](https://en.wikipedia.org/wiki/ActiveJDBC)
diff --git a/active-record/pom.xml b/active-record/pom.xml
new file mode 100644
index 000000000000..7664faea109d
--- /dev/null
+++ b/active-record/pom.xml
@@ -0,0 +1,30 @@
+
+
+
+ java-design-patterns
+ com.iluwatar
+ 1.6.0
+
+ 4.0.0
+
+ active-record
+
+
+
+
+ com.h2database
+ h2
+
+
+
+
+
+ junit
+ junit
+ test
+
+
+
+
diff --git a/active-record/src/main/java/com/iluwatar/active/record/DB.java b/active-record/src/main/java/com/iluwatar/active/record/DB.java
new file mode 100644
index 000000000000..2c8778488bff
--- /dev/null
+++ b/active-record/src/main/java/com/iluwatar/active/record/DB.java
@@ -0,0 +1,58 @@
+package com.iluwatar.active.record;
+
+import org.h2.tools.DeleteDbFiles;
+
+import java.sql.*;
+
+/**
+ * An utility class that holds and encapsulates all the DB related operations.
+ *
+ * Created by Stephen Lazarionok.
+ */
+public class DB {
+
+ static {
+ DeleteDbFiles.execute("~", "test", true);
+ try {
+ Class.forName("org.h2.Driver");
+ final Connection connection = getConnection();
+ final Statement statement = connection.createStatement();
+ statement.execute(
+ "create table wand(id BIGINT primary key, length_inches REAL, wood varchar(100), core varchar(100))");
+ statement.close();
+ connection.close();
+ } catch (final SQLException | ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Provides a connection to the database configured.
+ *
+ * @return
+ * @throws SQLException
+ */
+ public static Connection getConnection() throws SQLException {
+ return DriverManager.getConnection("jdbc:h2:~/test");
+ }
+
+ public static void closeConnection(final Connection connection) {
+ try {
+ if (connection != null) {
+ connection.close();
+ }
+ } catch (SQLException e) {
+ System.out.println("Failed to close a connection");
+ }
+ }
+
+ public static void closePreparedStatement(final PreparedStatement ps) {
+ try {
+ if (ps != null) {
+ ps.close();
+ }
+ } catch (SQLException e) {
+ System.out.println("Failed to close a prepared statement");
+ }
+ }
+}
diff --git a/active-record/src/main/java/com/iluwatar/active/record/MagicWand.java b/active-record/src/main/java/com/iluwatar/active/record/MagicWand.java
new file mode 100644
index 000000000000..21be0a34bf3f
--- /dev/null
+++ b/active-record/src/main/java/com/iluwatar/active/record/MagicWand.java
@@ -0,0 +1,279 @@
+package com.iluwatar.active.record;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+/**
+ * "Every single wand is unique and will depend for its character on the particular tree and magical creature
+ * from which it derives its materials. Moreover, each wand, from the moment it finds its ideal owner, will begin
+ * to learn from and teach its human partner." @ Garrick Ollivander
+ *
+ * Created by Stephen Lazarionok.
+ */
+public class MagicWand {
+
+ private Long id;
+
+ private double lengthInches;
+
+ private WandWoodType wood;
+
+ private WandCoreType core;
+
+ public Long getId() {
+ return id;
+ }
+
+ public double getLengthInches() {
+ return lengthInches;
+ }
+
+ public WandWoodType getWood() {
+ return wood;
+ }
+
+ public WandCoreType getCore() {
+ return core;
+ }
+
+ public void setLengthInches(double lengthInches) {
+ this.lengthInches = lengthInches;
+ }
+
+ public void setWood(WandWoodType wood) {
+ this.wood = wood;
+ }
+
+ public void setCore(WandCoreType core) {
+ this.core = core;
+ }
+
+ @Override public String toString() {
+ final StringBuilder sb = new StringBuilder("MagicWand(");
+ sb.append(getWood());
+ sb.append(", ");
+ sb.append(getCore());
+ sb.append(", ");
+ sb.append(getLengthInches());
+ sb.append(" inch(es))");
+ return sb.toString();
+ }
+
+ /**
+ * ***********************************
+ * ****** Data access methods
+ * ***********************************
+ */
+
+ private static final String SELECT_SQL = "select * from wand where id = ?";
+ private static final String DELETE_SQL = "delete from wand where id = ?";
+ private static final String UPDATE_SQL =
+ "update wand set length_inches = ?, wood = ?, core = ? where id = ?";
+ private static final String CREATE_SQL = "insert into wand values(?, ?, ?, ?)";
+
+ /**
+ * Saves the instance to the DB.
+ *
+ * @return
+ */
+ public long save() {
+ validateToSave();
+ Connection connection = null;
+ PreparedStatement ps = null;
+ try {
+ connection = DB.getConnection();
+ ps = connection.prepareStatement(CREATE_SQL);
+
+ final long id = System.currentTimeMillis();
+ ps.setLong(1, id);
+ ps.setDouble(2, getLengthInches());
+ ps.setString(3, getWood().name());
+ ps.setString(4, getCore().name());
+
+ ps.execute();
+
+
+ return id;
+ } catch (final SQLException e) {
+ throw new RuntimeException(e);
+ } finally {
+ DB.closePreparedStatement(ps);
+ DB.closeConnection(connection);
+ }
+ }
+
+ /**
+ * Deletes the instance from the DB.
+ */
+ public void delete() {
+ validateToDelete();
+ Connection connection = null;
+ PreparedStatement ps = null;
+ try {
+ connection = DB.getConnection();
+ ps = connection.prepareStatement(DELETE_SQL);
+
+
+ ps.setLong(1, getId());
+ ps.execute();
+ ps.close();
+ connection.close();
+ } catch (final SQLException e) {
+ throw new RuntimeException(e);
+ } finally {
+ DB.closePreparedStatement(ps);
+ DB.closeConnection(connection);
+ }
+ }
+
+ /**
+ * Updates the instance in DB.
+ */
+ public void update() {
+ validateToUpdate();
+ Connection connection = null;
+ PreparedStatement ps = null;
+ try {
+ connection = DB.getConnection();
+ ps = connection.prepareStatement(UPDATE_SQL);
+
+
+ ps.setDouble(1, getLengthInches());
+ ps.setString(2, getWood().name());
+ ps.setString(3, getCore().name());
+ ps.setLong(4, getId());
+
+ ps.execute();
+ ps.close();
+ connection.close();
+ } catch (final SQLException e) {
+ throw new RuntimeException(e);
+ } finally {
+ DB.closePreparedStatement(ps);
+ DB.closeConnection(connection);
+ }
+ }
+
+ /**
+ * Finds the instance in the DB.
+ *
+ * @param id
+ * @return
+ */
+ public static MagicWand find(long id) {
+ Connection connection = null;
+ PreparedStatement ps = null;
+ try {
+ connection = DB.getConnection();
+ ps = connection.prepareStatement(SELECT_SQL);
+
+ ResultSet rs;
+ ps.setLong(1, id);
+ rs = ps.executeQuery();
+ if (rs.next()) {
+ final MagicWand wand = new MagicWand();
+
+ wand.id = rs.getLong("id");
+ wand.setCore(WandCoreType.valueOf(rs.getString("core")));
+ wand.setWood(WandWoodType.valueOf(rs.getString("wood")));
+ wand.setLengthInches(rs.getDouble("length_inches"));
+
+ return wand;
+ } else {
+ return null;
+ }
+ } catch (final SQLException e) {
+ throw new RuntimeException(e);
+ } finally {
+ DB.closePreparedStatement(ps);
+ DB.closeConnection(connection);
+ }
+
+ }
+
+ private void validateToSave() {
+ validateProperties();
+ if (getId() != null)
+ throw new IllegalStateException(
+ "Can not save wand that was previously saved. Use 'update' metod instead.");
+ }
+
+ private void validateToUpdate() {
+ validateProperties();
+ if (getId() == null)
+ throw new IllegalStateException("Can not update a record without ID specified");
+ }
+
+ private void validateToDelete() {
+ if (getId() == null)
+ throw new IllegalStateException("Can not delete a record without ID specified");
+ }
+
+ private void validateProperties() {
+ if (getLengthInches() > 0.0d)
+ throw new IllegalStateException("Can not save a wand with length <= 0");
+ if (getWood() == null)
+ throw new IllegalStateException("Can not save a wand without wood specified");
+ if (getCore() == null)
+ throw new IllegalStateException("Can not save a wand without core specified");
+ }
+
+ /**
+ * ***********************************
+ * ****** Domain Logic
+ * ***********************************
+ */
+
+ private double calculateWoodMagicFactor() {
+
+ switch (getWood()) {
+ case HOLLY:
+ return 1.2d;
+ case WINE:
+ return 1.0d;
+ case HAWTHORN:
+ return 0.8d;
+ default:
+ return 0.0d;
+ }
+ }
+
+ private double calculateCoreMagicFactor() {
+
+ switch (getCore()) {
+ case PHOENIX_FEATHER:
+ return 1.2d;
+ case DRAGON_HEARTSTRING:
+ return 1.0d;
+ case UNICORN_HAIR:
+ return 0.8d;
+ default:
+ return 0.0d;
+ }
+ }
+
+ public double calculateMagicPower() {
+
+ return calculateWoodMagicFactor() * calculateWoodMagicFactor() * getLengthInches();
+ }
+
+ public void castFireball() throws SpellCastException {
+
+ if (calculateMagicPower() >= 10.0) {
+ System.out.println(toString() + ": a fireball spell is casted");
+ } else {
+ throw new SpellCastException("Can not cast fire ball! Not enough magic power...");
+ }
+ }
+
+ public void castLighting() throws SpellCastException {
+ if (calculateMagicPower() >= 20.0) {
+ System.out.println(toString() + ": a lighting spell is casted");
+ } else {
+ throw new SpellCastException("Can not cast lighting! Not enough magic power...");
+ }
+ }
+
+}
diff --git a/active-record/src/main/java/com/iluwatar/active/record/SpellCastException.java b/active-record/src/main/java/com/iluwatar/active/record/SpellCastException.java
new file mode 100644
index 000000000000..91066f94edc6
--- /dev/null
+++ b/active-record/src/main/java/com/iluwatar/active/record/SpellCastException.java
@@ -0,0 +1,18 @@
+package com.iluwatar.active.record;
+
+/**
+ * Describes the exception that is raised when a spell can not be cast for some reason.
+ *
+ * Created by Stephen Lazarionok.
+ */
+public class SpellCastException extends Exception {
+
+ /**
+ * Create an exception with the reason provided.
+ *
+ * @param message
+ */
+ public SpellCastException(final String message) {
+ super(message);
+ }
+}
diff --git a/active-record/src/main/java/com/iluwatar/active/record/WandCoreType.java b/active-record/src/main/java/com/iluwatar/active/record/WandCoreType.java
new file mode 100644
index 000000000000..5d1b806ce123
--- /dev/null
+++ b/active-record/src/main/java/com/iluwatar/active/record/WandCoreType.java
@@ -0,0 +1,15 @@
+package com.iluwatar.active.record;
+
+/**
+ * The core of a wand is a magical substance placed within the length of wood. They are usually
+ * bits of hair/feather extracted from some sort of Magical Being or Creature. The materials used
+ * for wand cores can vary widely, though certain wand-makers may prefer to use certain materials;
+ * for example, Garrick Ollivander discovered and pioneered the use of phoenix feathers, dragon heartstrings,
+ * and unicorn tail hairs.
+ *
+ * Created by Stephen Lazarionok.
+ */
+public enum WandCoreType {
+
+ PHOENIX_FEATHER, DRAGON_HEARTSTRING, UNICORN_HAIR
+}
diff --git a/active-record/src/main/java/com/iluwatar/active/record/WandWoodType.java b/active-record/src/main/java/com/iluwatar/active/record/WandWoodType.java
new file mode 100644
index 000000000000..b353da5b1722
--- /dev/null
+++ b/active-record/src/main/java/com/iluwatar/active/record/WandWoodType.java
@@ -0,0 +1,15 @@
+package com.iluwatar.active.record;
+
+/**
+ * Describes the wood types that can be used to produce magic wands.
+ *
+ * "Only a minority of trees can produce wand quality wood (just as a minority of humans can produce magic).
+ * It takes years of experience to tell which ones have the gift, although the job is made easier if Bowtruckles
+ * are found nesting in the leaves, as they never inhabit mundane trees." @ Garrick Ollivander
+ *
+ * Created by Stephen Lazarionok.
+ */
+public enum WandWoodType {
+
+ HOLLY, WINE, HAWTHORN
+}
diff --git a/active-record/src/main/java/com/iluwatar/active/record/sample/Application.java b/active-record/src/main/java/com/iluwatar/active/record/sample/Application.java
new file mode 100644
index 000000000000..e054d76d7b0b
--- /dev/null
+++ b/active-record/src/main/java/com/iluwatar/active/record/sample/Application.java
@@ -0,0 +1,35 @@
+package com.iluwatar.active.record.sample;
+
+import com.iluwatar.active.record.MagicWand;
+import com.iluwatar.active.record.SpellCastException;
+import com.iluwatar.active.record.WandCoreType;
+import com.iluwatar.active.record.WandWoodType;
+
+/**
+ * Created by Stephen Lazarionok.
+ */
+public class Application {
+
+ public static void main(final String[] args) throws SpellCastException {
+
+ final MagicWand harryPotterWand = new MagicWand();
+
+ harryPotterWand.setWood(WandWoodType.WINE);
+ harryPotterWand.setCore(WandCoreType.PHOENIX_FEATHER);
+ harryPotterWand.setLengthInches(11.0d);
+
+ final long wandId = harryPotterWand.save();
+
+ final MagicWand wand = MagicWand.find(wandId);
+
+ wand.castFireball();
+
+ wand.setCore(WandCoreType.DRAGON_HEARTSTRING);
+ wand.setLengthInches(21.0d);
+
+ wand.update();
+
+ MagicWand.find(wandId).castLighting();
+ MagicWand.find(wandId).delete();
+ }
+}
diff --git a/active-record/src/test/java/MagicWandSpecification.java b/active-record/src/test/java/MagicWandSpecification.java
new file mode 100644
index 000000000000..5f5b77ac9919
--- /dev/null
+++ b/active-record/src/test/java/MagicWandSpecification.java
@@ -0,0 +1,64 @@
+import com.iluwatar.active.record.MagicWand;
+import com.iluwatar.active.record.SpellCastException;
+import com.iluwatar.active.record.WandCoreType;
+import com.iluwatar.active.record.WandWoodType;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Created by Stephen Lazarionok.
+ */
+public class MagicWandSpecification {
+
+ @Rule public ExpectedException exception = ExpectedException.none();
+
+ @Test public void shouldCastFireball_IfMagicPower10AndMore() throws SpellCastException {
+
+ final MagicWand wand = new MagicWand();
+ wand.setWood(WandWoodType.WINE);
+ wand.setCore(WandCoreType.DRAGON_HEARTSTRING);
+ wand.setLengthInches(10.0d);
+
+ assertTrue(wand.calculateMagicPower() == 10.0d);
+ wand.castFireball();
+ }
+
+ @Test public void shouldNotCastFireball_IfMagicPowerLessThan10() throws SpellCastException {
+
+ final MagicWand wand = new MagicWand();
+ wand.setWood(WandWoodType.WINE);
+ wand.setCore(WandCoreType.DRAGON_HEARTSTRING);
+ wand.setLengthInches(9.0d);
+
+ assertTrue(wand.calculateMagicPower() == 9.0d);
+ exception.expect(SpellCastException.class);
+
+ wand.castFireball();
+ }
+
+ @Test public void shouldCastLigthing_IfMagicPower20AndMore() throws SpellCastException {
+
+ final MagicWand wand = new MagicWand();
+ wand.setWood(WandWoodType.WINE);
+ wand.setCore(WandCoreType.DRAGON_HEARTSTRING);
+ wand.setLengthInches(20.5d);
+
+ assertTrue(wand.calculateMagicPower() == 20.5d);
+ wand.castLighting();
+ }
+
+ @Test public void shouldNotCastLigthing_IfMagicPowerLessThan20() throws SpellCastException {
+
+ final MagicWand wand = new MagicWand();
+ wand.setWood(WandWoodType.WINE);
+ wand.setCore(WandCoreType.DRAGON_HEARTSTRING);
+ wand.setLengthInches(18.0d);
+
+ assertTrue(wand.calculateMagicPower() == 18.0d);
+ exception.expect(SpellCastException.class);
+ wand.castLighting();
+ }
+}
diff --git a/pom.xml b/pom.xml
index 1059edc58425..fa85344b046c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,6 +20,7 @@
abstract-factory
+ active-record
builder
factory-method
prototype