diff --git a/.gitignore b/.gitignore index bd55eacaf..589d3fb13 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,9 @@ target .idea *.iml *.swp +datanucleus.log +/bin/ +/bin/ +/bin/ +data-mapper/src/main/resources/log4j.xml \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 0bd0f0595..deb436cd2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,18 @@ language: java - jdk: - - oraclejdk8 +- oraclejdk8 -# whitelist -branches: - only: - - master +env: + global: + - GH_REF: github.com/iluwatar/java-design-patterns.git + - secure: LxTDuNS/rBWIvKkaEqr79ImZAe48mCdoYCF41coxNXgNoippo4GIBArknqtv+XvdkiuRZ1yGyj6pn8GU33c/yn+krddTUkVCwTbVatbalW5jhQjDbHYym/JcxaK9ZS/3JTeGcWrBgiPqHEEDhCf26vPZsXoMSeVCEORVKTp1BSg= + +before_install: +- export DISPLAY=:99.0 +- sh -e /etc/init.d/xvfb start after_success: - - mvn clean test jacoco:report coveralls:report \ No newline at end of file +- mvn clean test jacoco:report coveralls:report +- bash update-ghpages.sh + +sudo: false # route the build to the container-based infrastructure for a faster build diff --git a/CODE_COVERAGE.md b/CODE_COVERAGE.md new file mode 100644 index 000000000..589c7ad79 --- /dev/null +++ b/CODE_COVERAGE.md @@ -0,0 +1,13 @@ +# Code Coverage Report generation + +To generate the code coverage report, execute the following command: +> mvn clean verify + +This will generate code coverage report in each of the modules. In order to view the same, open the following file in your browser. +> target/site/jacoco/index.html + +Please note that the above folder is created under each of the modules. For example: +* adapter/target/site/jacoco/index.html +* busniess-delegate/target/site/jacoco/index.html + + diff --git a/CONTRIBUTING.MD b/CONTRIBUTING.MD new file mode 100644 index 000000000..39087fbf1 --- /dev/null +++ b/CONTRIBUTING.MD @@ -0,0 +1,4 @@ +This is great you have something to contribute! + +Before going any further please read the [wiki](https://github.com/iluwatar/java-design-patterns/wiki) +with conventions and rules we used for this project. diff --git a/LICENSE.md b/LICENSE.md index d1f75f80a..e73cf6618 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014 Ilkka Seppälä +Copyright (c) 2014-2016 Ilkka Seppälä Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 59312ea84..811d6a17a 100644 --- a/README.md +++ b/README.md @@ -1,738 +1,46 @@ -# Design pattern samples in Java. + -## Build status, coverage and static analysis: +# Design patterns implemented in Java -![Build status](https://travis-ci.org/iluwatar/java-design-patterns.svg?branch=master) [![Coverage Status](https://coveralls.io/repos/iluwatar/java-design-patterns/badge.svg?branch=master)](https://coveralls.io/r/iluwatar/java-design-patterns?branch=master) - Coverity Scan Build Status - +[![Build status](https://travis-ci.org/iluwatar/java-design-patterns.svg?branch=master)](https://travis-ci.org/iluwatar/java-design-patterns) +[![Coverage Status](https://coveralls.io/repos/iluwatar/java-design-patterns/badge.svg?branch=master)](https://coveralls.io/r/iluwatar/java-design-patterns?branch=master) +[![License MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/LICENSE.md) +[![Join the chat at https://gitter.im/iluwatar/java-design-patterns](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -## Introduction +# Introduction -Design patterns are formalized best practices that the programmer can use to solve common problems when designing an application or system. +Design patterns are formalized best practices that the programmer can use to +solve common problems when designing an application or system. -Design patterns can speed up the development process by providing tested, proven development paradigms. +Design patterns can speed up the development process by providing tested, proven +development paradigms. -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. +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 +# Getting started -### Creational Patterns +Before you dive into the material, you should be familiar with various +[Programming/Software Design Principles](http://webpro.github.io/programming-principles/). -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) -* [Multiton](#multiton) -* [Object Pool](#object-pool) - -### Structural Patterns - -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 - -Behavioral patterns are concerned with algorithms and the assignment of responsibilites 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 - -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) - -### Presentation Tier Patterns - -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) - -### Architectural Patterns - -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) - -### Integration Patterns - -Integration patterns are concerned with how software applications communicate and exchange data. - -* [Tolerant Reader](#tolerant-reader) - -### Idioms - -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) - -## Abstract Factory [↑](#list-of-design-patterns) -**Intent:** Provide an interface for creating families of related or dependent objects without specifying their concrete classes. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/abstract-factory/etc/abstract-factory_1.png "Abstract Factory") - -**Applicability:** Use the Abstract Factory pattern when -* a system should be independent of how its products are created, composed and represented -* a system should be configured with one of multiple families of products -* a family of related product objects is designed to be used together, and you need to enforce this constraint -* you want to provide a class library of products, and you want to reveal just their interfaces, not their implementations - -**Real world examples:** -* [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html) - -## Builder [↑](#list-of-design-patterns) -**Intent:** Separate the construction of a complex object from its representation so that the same construction process can create different representations. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/builder/etc/builder_1.png "Builder") - -**Applicability:** Use the Builder pattern when -* the algorithm for creating a complex object should be independent of the parts that make up the object and how they're assembled -* the construction process must allow different representations for the object that's constructed - -**Real world examples:** -* [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html) -* [Apache Camel builders](https://github.com/apache/camel/tree/0e195428ee04531be27a0b659005e3aa8d159d23/camel-core/src/main/java/org/apache/camel/builder) - -## Factory Method [↑](#list-of-design-patterns) -**Intent:** Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/factory-method/etc/factory-method_1.png "Factory Method") - -**Applicability:** Use the Factory Method pattern when -* a class can't anticipate the class of objects it must create -* a class wants its subclasses to specify the objects it creates -* classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate - -## Prototype [↑](#list-of-design-patterns) -**Intent:** Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/prototype/etc/prototype_1.png "Prototype") - -**Applicability:** Use the Prototype pattern when a system should be independent of how its products are created, composed and represented; and -* when the classes to instantiate are specified at run-time, for example, by dynamic loading; or -* to avoid building a class hierarchy of factories that parallels the class hierarchy of products; or -* when instances of a class can have one of only a few different combinations of state. It may be more convenient to install a corresponding number of prototypes and clone them rather than instantiating the class manually, each time with the appropriate state - -**Real world examples:** -* [java.lang.Object#clone()](http://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#clone%28%29) - -## Singleton [↑](#list-of-design-patterns) -**Intent:** Ensure a class only has one instance, and provide a global point of access to it. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/singleton/etc/singleton_1.png "Singleton") - -**Applicability:** Use the Singleton pattern when -* there must be exactly one instance of a class, and it must be accessible to clients from a well-known access point -* when the sole instance should be extensible by subclassing, and clients should be able to use an extended instance without modifying their code - -**Typical Use Case:** -* the logging class -* managing a connection to a database -* file manager - -**Real world examples:** -* [java.lang.Runtime#getRuntime()](http://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#getRuntime%28%29) - -## Adapter [↑](#list-of-design-patterns) -**Intent:** Convert the interface of a class into another interface the clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/adapter/etc/adapter_1.png "Adapter") - -**Applicability:** Use the Adapter pattern when -* you want to use an existing class, and its interface does not match the one you need -* you want to create a reusable class that cooperates with unrelated or unforeseen classes, that is, classes that don't necessarily have compatible interfaces -* you need to use several existing subclasses, but it's impractical to adapt their interface by subclassing every one. An object adapter can adapt the interface of its parent class. - -**Real world examples:** -* [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29) - -## Bridge [↑](#list-of-design-patterns) -**Intent:** Decouple an abstraction from its implementation so that the two can vary independently. - - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/bridge/etc/bridge_1.png "Bridge") - -**Applicability:** Use the Bridge pattern when -* you want to avoid a permanent binding between an abstraction and its implementation. This might be the case, for example, when the implementation must be selected or switched at run-time. -* both the abstractions and their implementations should be extensible by subclassing. In this case, the Bridge pattern lets you combine the different abstractions and implementations and extend them independently -* changes in the implementation of an abstraction should have no impact on clients; that is, their code should not have to be recompiled. -* you have a proliferation of classes. Such a class hierarchy indicates the need for splitting an object into two parts. Rumbaugh uses the term "nested generalizations" to refer to such class hierarchies -* you want to share an implementation among multiple objects (perhaps using reference counting), and this fact should be hidden from the client. A simple example is Coplien's String class, in which multiple objects can share the same string representation. - -## Composite [↑](#list-of-design-patterns) -**Intent:** Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/composite/etc/composite_1.png "Composite") - -**Applicability:** Use the Composite pattern when -* you want to represent part-whole hierarchies of objects -* you want clients to be able to ignore the difference between compositions of objects and individual objects. Clients will treat all objects in the composite structure uniformly - -**Real world examples:** -* [java.awt.Container](http://docs.oracle.com/javase/8/docs/api/java/awt/Container.html) and [java.awt.Component](http://docs.oracle.com/javase/8/docs/api/java/awt/Component.html) -* [Apache Wicket](https://github.com/apache/wicket) component tree, see [Component](https://github.com/apache/wicket/blob/91e154702ab1ff3481ef6cbb04c6044814b7e130/wicket-core/src/main/java/org/apache/wicket/Component.java) and [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java) - -## Decorator [↑](#list-of-design-patterns) -**Intent:** Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/decorator/etc/decorator_1.png "Decorator") - -**Applicability:** Use Decorator -* to add responsibilities to individual objects dynamically and transparently, that is, without affecting other objects -* for responsibilities that can be withdrawn -* when extension by subclassing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of sublasses to support every combination. Or a class definition may be hidden or otherwise unavailable for subclassing - -## Facade [↑](#list-of-design-patterns) -**Intent:** Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/facade/etc/facade_1.png "Facade") - -**Applicability:** Use the Facade pattern when -* you want to provide a simple interface to a complex subsystem. Subsystems often get more complex as they evolve. Most patterns, when applied, result in more and smaller classes. This makes the subsystem more reusable and easier to customize, but it also becomes harder to use for clients that don't need to customize it. A facade can provide a simple default view of the subsystem that is good enough for most clients. Only clients needing more customizability will need to look beyond the facade. -* there are many dependencies between clients and the implementation classes of an abstraction. Introduce a facade to decouple the subsystem from clients and other subsystems, thereby promoting subsystem independence and portability. -* you want to layer your subsystems. Use a facade to define an entry point to each subsystem level. If subsystems are dependent, the you can simplify the dependencies between them by making them communicate with each other solely through their facades - -## Flyweight [↑](#list-of-design-patterns) -**Intent:** Use sharing to support large numbers of fine-grained objects efficiently. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/flyweight/etc/flyweight_1.png "Flyweight") - -**Applicability:** The Flyweight pattern's effectiveness depends heavily on how and where it's used. Apply the Flyweight pattern when all of the following are true -* an application uses a large number of objects -* storage costs are high because of the sheer quantity of objects -* most object state can be made extrinsic -* many groups of objects may be replaced by relatively few shared objects once extrinsic state is removed -* the application doesn't depend on object identity. Since flyweight objects may be shared, identity tests will return true for conceptually distinct objects. - -**Real world examples:** -* [java.lang.Integer#valueOf(int)](http://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html#valueOf%28int%29) - -## Proxy [↑](#list-of-design-patterns) -**Intent:** Provide a surrogate or placeholder for another object to control access to it. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/proxy/etc/proxy_1.png "Proxy") - -**Applicability:** Proxy is applicable whenever there is a need for a more versatile or sophisticated reference to an object than a simple pointer. Here are several common situations in which the Proxy pattern is applicable - -* a remote proxy provides a local representative for an object in a different address space. -* a virtual proxy creates expensive objects on demand. -* a protection proxy controls access to the original object. Protection proxies are useful when objects should have different access rights. - -**Typical Use Case:** - -* Control access to another object -* Lazy initialization -* implement logging -* facilitate network connection -* to count references to an object - -**Real world examples:** -* [java.lang.reflect.Proxy](http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Proxy.html) -* [Apache Commons Proxy](https://commons.apache.org/proper/commons-proxy/) - -## Service Locator [↑](#list-of-design-patterns) -**Intent:** Encapsulate the processes involved in obtaining a service with a strong abstraction layer. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/service-locator/etc/service-locator.png "Proxy") - -**Applicability:** The service locator pattern is applicable whenever we want to locate/fetch various services using JNDI which, typically, is a redundant and expensive lookup. The service Locator pattern addresses this expensive lookup by making use of caching techniques ie. for the very first time a particular service is requested, the service Locator looks up in JNDI, fetched the relavant service and then finally caches this service object. Now, further lookups of the same service via Service Locator is done in its cache which improves the performance of application to great extent. - -**Typical Use Case:** - -* When network hits are expensive and time consuming -* lookups of services are done quite frequently -* large number of services are being used - -## Chain of responsibility [↑](#list-of-design-patterns) -**Intent:** Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/chain/etc/chain_1.png "Chain of Responsibility") - -**Applicability:** Use Chain of Responsibility when -* more than one object may handle a request, and the handler isn't known a priori. The handler should be ascertained automatically -* you want to issue a request to one of several objects without specifying the receiver explicitly -* the set of objects that can handle a request should be specified dynamically - -**Real world examples:** -* [java.util.logging.Logger#log()](http://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#log%28java.util.logging.Level,%20java.lang.String%29) -* [Apache Commons Chain](https://commons.apache.org/proper/commons-chain/index.html) - -## Command [↑](#list-of-design-patterns) -**Intent:** Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/command/etc/command.png "Command") - -**Applicability:** Use the Command pattern when you want to - -* parameterize objects by an action to perform. You can express such parameterization in a procedural language with a callback function, that is, a function that's registered somewhere to be called at a later point. Commands are an object-oriented replacement for callbacks. -* specify, queue, and execute requests at different times. A Command object can have a lifetime independent of the original request. If the receiver of a request can be represented in an address space-independent way, then you can transfer a command object for the request to a different process and fulfill the request there -* support undo. The Command's execute operation can store state for reversing its effects in the command itself. The Command interface must have an added Unexecute operation that reverses the effects of a previous call to execute. Executed commands are stored in a history list. Unlimited-level undo and redo is achieved by traversing this list backwards and forwards calling unexecute and execute, respectively -* support logging changes so that they can be reapplied in case of a system crash. By augmenting the Command interface with load and store operations, you can keep a persistent log of changes. Recovering from a crash involves reloading logged commands from disk and re-executing them with the execute operation -* structure a system around high-level operations build on primitive operations. Such a structure is common in information systems that support transactions. A transaction encapsulates a set of changes to data. The Command pattern offers a way to model transactions. Commands have a common interface, letting you invoke all transactions the same way. The pattern also makes it easy to extend the system with new transactions - -**Typical Use Case:** - -* to keep a history of requests -* implement callback functionality -* implement the undo functionality - -**Real world examples:** -* [java.lang.Runnable](http://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html) - -## Interpreter [↑](#list-of-design-patterns) -**Intent:** Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/interpreter/etc/interpreter_1.png "Interpreter") - -**Applicability:** Use the Interpreter pattern when there is a language to interpret, and you can represent statements in the language as abstract syntax trees. The Interpreter pattern works best when -* the grammar is simple. For complex grammars, the class hierarchy for the grammar becomes large and unmanageable. Tools such as parser generators are a better alternative in such cases. They can interpret expressions without building abstract syntax trees, which can save space and possibly time -* efficiency is not a critical concern. The most efficient interpreters are usually not implemented by interpreting parse trees directly but by first translating them into another form. For example, regular expressions are often transformed into state machines. But even then, the translator can be implemented by the Interpreter pattern, so the pattern is still applicable - -## Iterator [↑](#list-of-design-patterns) -**Intent:** Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/iterator/etc/iterator_1.png "Iterator") - -**Applicability:** Use the Iterator pattern -* to access an aggregate object's contents without exposing its internal representation -* to support multiple traversals of aggregate objects -* to provide a uniform interface for traversing different aggregate structures - -**Real world examples:** -* [java.util.Iterator](http://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html) - -## Mediator [↑](#list-of-design-patterns) -**Intent:** Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/mediator/etc/mediator_1.png "Mediator") - -**Applicability:** Use the Mediator pattern when -* a set of objects communicate in well-defined but complex ways. The resulting interdependencies are unstructured and difficult to understand -* reusing an object is difficult because it refers to and communicates with many other objects -* a behavior that's distributed between several classes should be customizable without a lot of subclassing - -## Memento [↑](#list-of-design-patterns) -**Intent:** Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/memento/etc/memento.png "Memento") - -**Applicability:** Use the Memento pattern when -* a snapshot of an object's state must be saved so that it can be restored to that state later, and -* a direct interface to obtaining the state would expose implementation details and break the object's encapsulation - -**Real world examples:** -* [java.util.Date](http://docs.oracle.com/javase/8/docs/api/java/util/Date.html) - -## Observer [↑](#list-of-design-patterns) -**Intent:** Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/observer/etc/observer_1.png "Observer") - -**Applicability:** Use the Observer pattern in any of the following situations - -* when an abstraction has two aspects, one dependent on the other. Encapsulating these aspects in separate objects lets you vary and reuse them independently -* when a change to one object requires changing others, and you don't know how many objects need to be changed -* when an object should be able to notify other objects without making assumptions about who these objects are. In other words, you don't want these objects tightly coupled - -**Typical Use Case:** - -* changing in one object leads to a change in other objects - -**Real world examples:** -* [java.util.Observer](http://docs.oracle.com/javase/8/docs/api/java/util/Observer.html) - -## State [↑](#list-of-design-patterns) -**Intent:** Allow an object to alter its behavior when its internal state changes. The object will appear to change its class. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/state/etc/state_1.png "State") - -**Applicability:** Use the State pattern in either of the following cases -* an object's behavior depends on its state, and it must change its behavior at run-time depending on that state -* operations have large, multipart conditional statements that depend on the object's state. This state is usually represented by one or more enumerated constants. Often, several operations will contain this same conditional structure. The State pattern puts each branch of the conditional in a separate class. This lets you treat the object's state as an object in its own right that can vary independently from other objects. - -## Strategy [↑](#list-of-design-patterns) -**Intent:** Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/strategy/etc/strategy_1.png "Strategy") - -**Applicability:** Use the Strategy pattern when -* many related classes differ only in their behavior. Strategies provide a way to configure a class either one of many behaviors -* you need different variants of an algorithm. for example, you might define algorithms reflecting different space/time trade-offs. Strategies can be used when these variants are implemented as a class hierarchy of algorithms -* an algorithm uses data that clients shouldn't know about. Use the Strategy pattern to avoid exposing complex, algorithm-specific data structures -* a class defines many behaviors, and these appear as multiple conditional statements in its operations. Instead of many conditionals, move related conditional branches into their own Strategy class - -## Template method [↑](#list-of-design-patterns) -**Intent:** Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/template-method/etc/template-method_1.png "Template Method") - -**Applicability:** The Template Method pattern should be used -* to implement the invariant parts of an algorithm once and leave it up to subclasses to implement the behavior that can vary -* when common behavior among subclasses should be factored and localized in a common class to avoid code duplication. This is good example of "refactoring to generalize" as described by Opdyke and Johnson. You first identify the differences in the existing code and then separate the differences into new operations. Finally, you replace the differing code with a template method that calls one of these new operations -* to control subclasses extensions. You can define a template method that calls "hook" operations at specific points, thereby permitting extensions only at those points - -## Visitor [↑](#list-of-design-patterns) -**Intent:** Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/visitor/etc/visitor_1.png "Visitor") - -**Applicability:** Use the Visitor pattern when -* an object structure contains many classes of objects with differing interfaces, and you want to perform operations on these objects that depend on their concrete classes -* many distinct and unrelated operations need to be performed on objects in an object structure, and you want to avoid "polluting" their classes with these operations. Visitor lets you keep related operations together by defining them in one class. When the object structure is shared by many applications, use Visitor to put operations in just those applications that need them -* the classes defining the object structure rarely change, but you often want to define new operations over the structure. Changing the object structure classes requires redefining the interface to all visitors, which is potentially costly. If the object structure classes change often, then it's probably better to define the operations in those classes - -**Real world examples:** -* [Apache Wicket](https://github.com/apache/wicket) component tree, see [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java) - -## Model-View-Presenter [↑](#list-of-design-patterns) -**Intent:** Apply a "Separation of Concerns" principle in a way that allows developers to build and test user interfaces. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/model-view-presenter/etc/model-view-presenter_1.png "Model-View-Presenter") - -**Applicability:** Use the Model-View-Presenter in any of the following situations -* when you want to improve the "Separation of Concerns" principle in presentation logic -* when a user interface development and testing is necessary. - -## Data Access Object [↑](#list-of-design-patterns) -**Intent:** Object provides an abstract interface to some type of database or other persistence mechanism. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/dao/etc/dao.png "Data Access Object") - -**Applicability:** Use the Data Access Object in any of the following situations -* when you want to consolidate how the data layer is accessed -* when you want to avoid writing multiple data retrieval/persistence layers - -## Double Checked Locking [↑](#list-of-design-patterns) -**Intent:** Reduce the overhead of acquiring a lock by first testing the locking criterion (the "lock hint") without actually acquiring the lock. Only if the locking criterion check indicates that locking is required does the actual locking logic proceed. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/double-checked-locking/etc/double_checked_locking_1.png "Double Checked Locking") - -**Applicability:** Use the Double Checked Locking pattern when -* there is a concurrent access in object creation, e.g. singleton, where you want to create single instance of the same class and checking if it's null or not maybe not be enough when there are two or more threads that checks if instance is null or not. -* there is a concurrent access on a method where method's behaviour changes according to the some constraints and these constraint change within this method. - -## Servant [↑](#list-of-design-patterns) -**Intent:** Servant is used for providing some behavior to a group of classes. Instead of defining that behavior in each class - or when we cannot factor out this behavior in the common parent class - it is defined once in the Servant. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/servant/etc/servant-pattern.png "Servant") - -**Applicability:** Use the Servant pattern when -* When we want some objects to perform a common action and don't want to define this action as a method in every class. - -## Null Object [↑](#list-of-design-patterns) -**Intent:** In most object-oriented languages, such as Java or C#, references may be null. These references need to be checked to ensure they are not null before invoking any methods, because methods typically cannot be invoked on null references. Instead of using a null reference to convey absence of an object (for instance, a non-existent customer), one uses an object which implements the expected interface, but whose method body is empty. The advantage of this approach over a working default implementation is that a Null Object is very predictable and has no side effects: it does nothing. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/null-object/etc/null-object.png "Null Object") - -**Applicability:** Use the Null Object pattern when -* You want to avoid explicit null checks and keep the algorithm elegant and easy to read. - -## Event Aggregator [↑](#list-of-design-patterns) -**Intent:** A system with lots of objects can lead to complexities when a client wants to subscribe to events. The client has to find and register for each object individually, if each object has multiple events then each event requires a separate subscription. An Event Aggregator acts as a single source of events for many objects. It registers for all the events of the many objects allowing clients to register with just the aggregator. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/event-aggregator/etc/classes.png "Event Aggregator") - -**Applicability:** Use the Event Aggregator pattern when -* Event Aggregator is a good choice when you have lots of objects that are potential event sources. Rather than have the observer deal with registering with them all, you can centralize the registration logic to the Event Aggregator. As well as simplifying registration, a Event Aggregator also simplifies the memory management issues in using observers. - -## Callback [↑](#list-of-design-patterns) -**Intent:** Callback is a piece of executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at some convenient time. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/callback/etc/callback.png "Callback") - -**Applicability:** Use the Callback pattern when -* When some arbitrary synchronous or asynchronous action must be performed after execution of some defined activity. - -**Real world examples:** -* [CyclicBarrier] (http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html#CyclicBarrier%28int,%20java.lang.Runnable%29) constructor can accept callback that will be triggered every time when barrier is tripped. - -## Intercepting Filter [↑](#list-of-design-patterns) -**Intent:** Provide pluggable filters to conduct necessary pre-processing and post-processing to requests from a client to a target - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/intercepting-filter/etc/intercepting-filter.png "Intercepting Filter") - -**Applicability:** Use the Intercepting Filter pattern when -* a system uses pre-processing or post-processing requests -* a system should do the authentication/ authorization/ logging or tracking of request and then pass the requests to corresponding handlers -* you want a modular approach to configuring pre-processing and post-processing schemes - -## Execute Around [↑](#list-of-design-patterns) -**Intent:** Execute Around idiom frees the user from certain actions that should always be executed before and after the business method. A good example of this is resource allocation and deallocation leaving the user to specify only what to do with the resource. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/execute-around/etc/execute-around.png "Execute Around") - -**Applicability:** Use the Execute Around idiom when -* You use an API that requires methods to be called in pairs such as open/close or allocate/deallocate. - -## Property [↑](#list-of-design-patterns) -**Intent:** Create hierarchy of objects and new objects using already existing objects as parents. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/property/etc/property.png "Property") - -**Applicability:** Use the Property pattern when -* when you like to have objects with dynamic set of fields and prototype inheritance - -**Real world examples:** -* [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain) prototype inheritance - -## Poison Pill [↑](#list-of-design-patterns) -**Intent:** Poison Pill is known predefined data item that allows to provide graceful shutdown for separate distributed consumption process. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/poison-pill/etc/poison-pill.png "Poison Pill") - -**Applicability:** Use the Poison Pill idiom when -* need to send signal from one thread/process to another to terminate - -**Real world examples:** -* [akka.actor.PoisonPill](http://doc.akka.io/docs/akka/2.1.4/java/untyped-actors.html) - -## Lazy Loading [↑](#list-of-design-patterns) -**Intent:** Lazy loading is a design pattern commonly used to defer initialization of an object until the point at which it is needed. It can contribute to efficiency in the program's operation if properly and appropriately used. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/lazy-loading/etc/lazy-loading.png "Lazy Loading") - -**Applicability:** Use the Lazy Loading idiom when -* eager loading is expensive or the object to be loaded might not be needed at all - -**Real world examples:** -* JPA annotations @OneToOne, @OneToMany, @ManyToOne, @ManyToMany and fetch = FetchType.LAZY - -## Service Layer [↑](#list-of-design-patterns) -**Intent:** Service Layer is an abstraction over domain logic. Typically applications require multiple kinds of interfaces to the data -they store and logic they implement: data loaders, user interfaces, integration gateways, and others. Despite their different purposes, these interfaces often need common interactions with the application to access and manipulate its data and invoke its business logic. The Service Layer fulfills this role. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/service-layer/etc/service-layer.png "Service Layer") - -**Applicability:** Use the Service Layer pattern when -* you want to encapsulate domain logic under API -* you need to implement multiple interfaces with common logic and data - -## Specification [↑](#list-of-design-patterns) -**Intent:** Specification pattern separates the statement of how to match a candidate, from the -candidate object that it is matched against. As well as its usefulness in selection, it is also valuable for -validation and for building to order - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/specification/etc/specification.png "Specification") - -**Applicability:** Use the Specification pattern when -* You need to select a subset of objects based on some criteria, and to refresh the selection at various times -* You need to check that only suitable objects are used for a certain role (validation) - -## Tolerant Reader [↑](#list-of-design-patterns) -**Intent:** Tolerant Reader is an integration pattern that helps creating robust communication systems. The idea is to be as tolerant as possible when reading data from another service. This way, when the communication schema changes, the readers must not break. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/tolerant-reader/etc/tolerant-reader.png "Tolerant Reader") - -**Applicability:** Use the Tolerant Reader pattern when -* The communication schema can evolve and change and yet the receiving side should not break - -## Model-View-Controller [↑](#list-of-design-patterns) -**Intent:** Separate the user interface into three interconnected components: the model, the view and the controller. Let the model manage the data, the view display the data and the controller mediate updating the data and redrawing the display. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/model-view-controller/etc/model-view-controller.png "Model-View-Controller") - -**Applicability:** Use the Model-View-Controller pattern when -* you want to clearly separate the domain data from its user interface representation - -## Flux [↑](#list-of-design-patterns) -**Intent:** Flux eschews MVC in favor of a unidirectional data flow. When a user interacts with a view, the view propagates an action through a central dispatcher, to the various stores that hold the application's data and business logic, which updates all of the views that are affected. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/flux/etc/flux.png "Flux") - -**Applicability:** Use the Flux pattern when -* You want to focus on creating explicit and understandable update paths for your application's data, which makes tracing changes during development simpler and makes bugs easier to track down and fix. - -## Double Dispatch [↑](#list-of-design-patterns) -**Intent:** Double Dispatch pattern is a way to create maintainable dynamic behavior based on receiver and parameter types. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/double-dispatch/etc/double-dispatch.png "Double Dispatch") - -**Applicability:** Use the Double Dispatch pattern when -* The dynamic behavior is not defined only based on receiving object's type but also on the receiving method's parameter type. - -**Real world examples:** -* [ObjectOutputStream](https://docs.oracle.com/javase/8/docs/api/java/io/ObjectOutputStream.html) - -## Multiton [↑](#list-of-design-patterns) -**Intent:** Ensure a class only has limited number of instances, and provide a global point of access to them. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/multiton/etc/multiton.png "Multiton") - -**Applicability:** Use the Multiton pattern when -* there must be specific number of instances of a class, and they must be accessible to clients from a well-known access point - -## Resource Acquisition Is Initialization [↑](#list-of-design-patterns) -**Intent:** Resource Acquisition Is Initialization pattern can be used to implement exception safe resource management. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/resource-acquisition-is-initialization/etc/resource-acquisition-is-initialization.png "Resource Acquisition Is Initialization") - -**Applicability:** Use the Resource Acquisition Is Initialization pattern when -* You have resources that must be closed in every condition - -## Thread Pool [↑](#list-of-design-patterns) -**Intent:** It is often the case that tasks to be executed are short-lived and the number of tasks is large. Creating a new thread for each task would make the system spend more time creating and destroying the threads than executing the actual tasks. Thread Pool solves this problem by reusing existing threads and eliminating the latency of creating new threads. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/thread-pool/etc/thread-pool.png "Thread Pool") - -**Applicability:** Use the Thread Pool pattern when -* You have a large number of short-lived tasks to be executed in parallel - -## Private Class Data [↑](#list-of-design-patterns) -**Intent:** Private Class Data design pattern seeks to reduce exposure of attributes by limiting their visibility. It reduces the number of class attributes by encapsulating them in single Data object. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/private-class-data/etc/private-class-data.png "Private Class Data") - -**Applicability:** Use the Private Class Data pattern when -* You want to prevent write access to class data members - -## Object Pool [↑](#list-of-design-patterns) -**Intent:** When objects are expensive to create and they are needed only for short periods of time it is advantageous to utilize the Object Pool pattern. The Object Pool provides a cache for instantiated objects tracking which ones are in use and which are available. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/object-pool/etc/object-pool.png "Object Pool") - -**Applicability:** Use the Object Pool pattern when -* The objects are expensive to create (allocation cost) -* You need a large number of short-lived objects (memory fragmentation) - -## Dependency Injection [↑](#list-of-design-patterns) -**Intent:** Dependency Injection is a software design pattern in which one or more dependencies (or services) are injected, or passed by reference, into a dependent object (or client) and are made part of the client's state. The pattern separates the creation of a client's dependencies from its own behavior, which allows program designs to be loosely coupled and to follow the inversion of control and single responsibility principles. - -![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/dependency-injection/etc/dependency-injection.png "Dependency Injection") - -**Applicability:** Use the Dependency Injection pattern when -* When you need to remove knowledge of concrete implementation from object -* To enable unit testing of classes in isolation using mock objects or stubs - - - -# Frequently asked questions - -**Q: What is the difference between State and Strategy patterns?** - -While the implementation is similar they solve different problems. The State pattern deals with what state an object is in - it encapsulates state-dependent behavior. The Strategy pattern deals with how an object performs a certain task - it encapsulates an algorithm. - -**Q: What is the difference between Strategy and Template Method patterns?** - -In Template Method the algorithm is chosen at compile time via inheritance. With Strategy pattern the algorithm is chosen at runtime via composition. - -**Q: What is the difference between Proxy and Decorator patterns?** - -The difference is the intent of the patterns. While Proxy controls access to the object Decorator is used to add responsibilities to the object. - -**Q: What is the difference between Chain of Responsibility and Intercepting Filter patterns?** - -While the implementations look similar there are differences. The Chain of Responsibility forms a chain of request processors and the processors are then executed one by one until the correct processor is found. In Intercepting Filter the chain is constructed from filters and the whole chain is always executed. - -**Q: What is the difference between Visitor and Double Dispatch patterns?** - -The Visitor pattern is a means of adding a new operation to existing classes. Double dispatch is a means of dispatching function calls with respect to two polymorphic types, rather than a single polymorphic type, which is what languages like C++ and Java support directly. - -**Q: What are the differences between Flyweight and Object Pool patterns?** - -They differ in the way they are used. - -Pooled objects can simultaneously be used by a single "client" only. For that, a pooled object must be checked out from the pool, then it can be used by a client, and then the client must return the object back to the pool. Multiple instances of identical objects may exist, up to the maximal capacity of the pool. - -In contrast, a Flyweight object is singleton, and it can be used simultaneously by multiple clients. - -As for concurrent access, pooled objects can be mutable and they usually don't need to be thread safe, as typically, only one thread is going to use a specific instance at the same time. Flyweight must either be immutable (the best option), or implement thread safety. - -As for performance and scalability, pools can become bottlenecks, if all the pooled objects are in use and more clients need them, threads will become blocked waiting for available object from the pool. This is not the case with Flyweight. +All designs should be as simple as possible. You should start with KISS, YAGNI, +and Do The Simplest Thing That Could Possibly Work principles. Complexity and +patterns should only be introduced when they are needed for practical +extensibility. +Once you are familiar with these concepts you can start drilling down into +patterns by any of the following approaches + - Using difficulty tags, `Difficulty-Beginner`, `Difficulty-Intermediate` & `Difficulty-Expert`. + - Using pattern categories, `Creational`, `Behavioral` and others. + - Search for a specific pattern. Can't find one? Please report a new pattern [here](https://github.com/iluwatar/java-design-patterns/issues). # How to contribute -**To add a new pattern** you need to do the following steps: - -1. Fork the repository. -2. Implement the code changes in your fork. Remember to add sufficient comments documenting the implementation. -3. Create a simple class diagram from your example code. -4. Add description of the pattern in README.md and link to the class diagram. -5. Create a pull request. - -**To work on one of the raised issues** you need to do the following steps: - -1. Fork the repository. -2. 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. -3. 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 - -Java-design-patterns project uses [semantic versioning](http://semver.org/) scheme. - - - -# Credits - -* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) -* [Effective Java (2nd Edition)](http://www.amazon.com/Effective-Java-Edition-Joshua-Bloch/dp/0321356683) -* [Java Generics and Collections](http://www.amazon.com/Java-Generics-Collections-Maurice-Naftalin/dp/0596527756/) -* [Let’s Modify the Objects-First Approach into Design-Patterns-First](http://edu.pecinovsky.cz/papers/2006_ITiCSE_Design_Patterns_First.pdf) -* [Pattern Languages of Program Design](http://www.amazon.com/Pattern-Languages-Program-Design-Coplien/dp/0201607344/ref=sr_1_1) -* [Martin Fowler - Event Aggregator](http://martinfowler.com/eaaDev/EventAggregator.html) -* [TutorialsPoint - Intercepting Filter](http://www.tutorialspoint.com/design_pattern/intercepting_filter_pattern.htm) -* [Presentation Tier Patterns](http://www.javagyan.com/tutorials/corej2eepatterns/presentation-tier-patterns) -* [Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions](http://www.amazon.com/Functional-Programming-Java-Harnessing-Expressions/dp/1937785467/ref=sr_1_1) -* [Martin Fowler - Service Layer](http://martinfowler.com/eaaCatalog/serviceLayer.html) -* [Martin Fowler - Specifications](http://martinfowler.com/apsupp/spec.pdf) -* [Martin Fowler - Tolerant Reader](http://martinfowler.com/bliki/TolerantReader.html) -* [Trygve Reenskaug - Model-view-controller](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) -* [Flux - Application architecture for building user interfaces](http://facebook.github.io/flux/) - - +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). We will help you and answer your questions in the [Gitter chatroom](https://gitter.im/iluwatar/java-design-patterns). # License diff --git a/abstract-document/README.md b/abstract-document/README.md new file mode 100644 index 000000000..bf28ff999 --- /dev/null +++ b/abstract-document/README.md @@ -0,0 +1,31 @@ +--- +layout: pattern +title: Abstract Document +folder: abstract-document +permalink: /patterns/abstract-document/ +categories: Structural +tags: + - Java + - Difficulty-Intermediate +--- + +## Intent +Achieve flexibility of untyped languages and keep the type-safety + +![alt text](./etc/abstract-document-base.png "Abstract Document Base") + +![alt text](./etc/abstract-document.png "Abstract Document Traits and Domain") + + +## Applicability +Use the Abstract Document Pattern when + +* there is a need to add new properties on the fly +* you want a flexible way to organize domain in tree like structure +* you want more loosely coupled system + + +## Credits + +* [Wikipedia: Abstract Document Pattern](https://en.wikipedia.org/wiki/Abstract_Document_Pattern) +* [Martin Fowler: Dealing with properties](http://martinfowler.com/apsupp/properties.pdf) \ No newline at end of file diff --git a/abstract-document/etc/abstract-document-base.png b/abstract-document/etc/abstract-document-base.png new file mode 100644 index 000000000..13345dbb8 Binary files /dev/null and b/abstract-document/etc/abstract-document-base.png differ diff --git a/abstract-document/etc/abstract-document-base.ucls b/abstract-document/etc/abstract-document-base.ucls new file mode 100644 index 000000000..bfe927ed9 --- /dev/null +++ b/abstract-document/etc/abstract-document-base.ucls @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/abstract-document/etc/abstract-document.png b/abstract-document/etc/abstract-document.png new file mode 100644 index 000000000..98d186f7e Binary files /dev/null and b/abstract-document/etc/abstract-document.png differ diff --git a/abstract-document/etc/abstract-document.ucls b/abstract-document/etc/abstract-document.ucls new file mode 100644 index 000000000..ad97457fd --- /dev/null +++ b/abstract-document/etc/abstract-document.ucls @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/abstract-document/pom.xml b/abstract-document/pom.xml new file mode 100644 index 000000000..b7a348d26 --- /dev/null +++ b/abstract-document/pom.xml @@ -0,0 +1,42 @@ + + + + 4.0.0 + + java-design-patterns + com.iluwatar + 1.13.0-SNAPSHOT + + abstract-document + + + junit + junit + test + + + \ No newline at end of file diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java new file mode 100644 index 000000000..4bf8f0d14 --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java @@ -0,0 +1,72 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractdocument; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; + +/** + * Abstract implementation of Document interface + */ +public abstract class AbstractDocument implements Document { + + private final Map properties; + + protected AbstractDocument(Map properties) { + Objects.requireNonNull(properties, "properties map is required"); + this.properties = properties; + } + + @Override + public Void put(String key, Object value) { + properties.put(key, value); + return null; + } + + @Override + public Object get(String key) { + return properties.get(key); + } + + @Override + public Stream children(String key, Function, T> constructor) { + Optional>> any = Stream.of(get(key)).filter(el -> el != null) + .map(el -> (List>) el).findAny(); + return any.isPresent() ? any.get().stream().map(constructor) : Stream.empty(); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(getClass().getName()).append("["); + properties.entrySet() + .forEach(e -> builder.append("[").append(e.getKey()).append(" : ").append(e.getValue()).append("]")); + builder.append("]"); + return builder.toString(); + } + +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java new file mode 100644 index 000000000..d7758b6f7 --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java @@ -0,0 +1,88 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractdocument; + +import com.iluwatar.abstractdocument.domain.Car; +import com.iluwatar.abstractdocument.domain.HasModel; +import com.iluwatar.abstractdocument.domain.HasParts; +import com.iluwatar.abstractdocument.domain.HasPrice; +import com.iluwatar.abstractdocument.domain.HasType; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * The Abstract Document pattern enables handling additional, non-static + * properties. This pattern uses concept of traits to enable type safety and + * separate properties of different classes into set of interfaces. + *

+ *

+ * In Abstract Document pattern,({@link AbstractDocument}) fully implements + * {@link Document}) interface. Traits are then defined to enable access to + * properties in usual, static way. + */ +public class App { + + /** + * Executes the App + */ + public App() { + System.out.println("Constructing parts and car"); + + Map carProperties = new HashMap<>(); + carProperties.put(HasModel.PROPERTY, "300SL"); + carProperties.put(HasPrice.PROPERTY, 10000L); + + Map wheelProperties = new HashMap<>(); + wheelProperties.put(HasType.PROPERTY, "wheel"); + wheelProperties.put(HasModel.PROPERTY, "15C"); + wheelProperties.put(HasPrice.PROPERTY, 100L); + + Map doorProperties = new HashMap<>(); + doorProperties.put(HasType.PROPERTY, "door"); + doorProperties.put(HasModel.PROPERTY, "Lambo"); + doorProperties.put(HasPrice.PROPERTY, 300L); + + carProperties.put(HasParts.PROPERTY, Arrays.asList(wheelProperties, doorProperties)); + + Car car = new Car(carProperties); + + System.out.println("Here is our car:"); + System.out.println("-> model: " + car.getModel().get()); + System.out.println("-> price: " + car.getPrice().get()); + System.out.println("-> parts: "); + car.getParts().forEach(p -> System.out + .println("\t" + p.getType().get() + "/" + p.getModel().get() + "/" + p.getPrice().get())); + } + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + new App(); + } + +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java new file mode 100644 index 000000000..7705f37eb --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java @@ -0,0 +1,59 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractdocument; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Stream; + +/** + * Document interface + */ +public interface Document { + + /** + * Puts the value related to the key + * + * @param key element key + * @param value element value + * @return Void + */ + Void put(String key, Object value); + + /** + * Gets the value for the key + * + * @param key element key + * @return value or null + */ + Object get(String key); + + /** + * Gets the stream of child documents + * + * @param key element key + * @param constructor constructor of child class + * @return child documents + */ + Stream children(String key, Function, T> constructor); +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java new file mode 100644 index 000000000..e29ee63da --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractdocument.domain; + +import java.util.Map; + +import com.iluwatar.abstractdocument.AbstractDocument; + +/** + * Car entity + */ +public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts { + + public Car(Map properties) { + super(properties); + } + +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java new file mode 100644 index 000000000..fbd8c0d7f --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractdocument.domain; + +import java.util.Optional; + +import com.iluwatar.abstractdocument.Document; + +/** + * HasModel trait for static access to 'model' property + */ +public interface HasModel extends Document { + + String PROPERTY = "model"; + + default Optional getModel() { + return Optional.ofNullable((String) get(PROPERTY)); + } + +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java new file mode 100644 index 000000000..581702cc9 --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractdocument.domain; + +import java.util.stream.Stream; + +import com.iluwatar.abstractdocument.Document; + +/** + * HasParts trait for static access to 'parts' property + */ +public interface HasParts extends Document { + + String PROPERTY = "parts"; + + default Stream getParts() { + return children(PROPERTY, Part::new); + } + +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java new file mode 100644 index 000000000..3d1d0e3e7 --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractdocument.domain; + +import java.util.Optional; + +import com.iluwatar.abstractdocument.Document; + +/** + * HasPrice trait for static access to 'price' property + */ +public interface HasPrice extends Document { + + String PROPERTY = "price"; + + default Optional getPrice() { + return Optional.ofNullable((Number) get(PROPERTY)); + } + +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java new file mode 100644 index 000000000..b0f292bb6 --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractdocument.domain; + +import com.iluwatar.abstractdocument.Document; + +import java.util.Optional; + +/** + * HasType trait for static access to 'type' property + */ +public interface HasType extends Document { + + String PROPERTY = "type"; + + default Optional getType() { + return Optional.ofNullable((String) get(PROPERTY)); + } + +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java new file mode 100644 index 000000000..e42f099d9 --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractdocument.domain; + +import java.util.Map; + +import com.iluwatar.abstractdocument.AbstractDocument; + +/** + * Part entity + */ +public class Part extends AbstractDocument implements HasType, HasModel, HasPrice { + + public Part(Map properties) { + super(properties); + } + +} diff --git a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java new file mode 100644 index 000000000..b6467e232 --- /dev/null +++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java @@ -0,0 +1,88 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractdocument; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertNotNull; + +/** + * AbstractDocument test class + */ +public class AbstractDocumentTest { + + private static final String KEY = "key"; + private static final String VALUE = "value"; + + private class DocumentImplementation extends AbstractDocument { + + DocumentImplementation(Map properties) { + super(properties); + } + } + + private DocumentImplementation document = new DocumentImplementation(new HashMap<>()); + + @Test + public void shouldPutAndGetValue() { + document.put(KEY, VALUE); + assertEquals(VALUE, document.get(KEY)); + } + + @Test + public void shouldRetrieveChildren() { + Map child1 = new HashMap<>(); + Map child2 = new HashMap<>(); + List> children = Arrays.asList(child1, child2); + + document.put(KEY, children); + + Stream childrenStream = document.children(KEY, DocumentImplementation::new); + assertNotNull(children); + assertEquals(2, childrenStream.count()); + } + + @Test + public void shouldRetrieveEmptyStreamForNonExistingChildren() { + Stream children = document.children(KEY, DocumentImplementation::new); + assertNotNull(children); + assertEquals(0, children.count()); + } + + @Test + public void shouldIncludePropsInToString() { + Map props = new HashMap<>(); + props.put(KEY, VALUE); + DocumentImplementation document = new DocumentImplementation(props); + assertNotNull(document.toString().contains(KEY)); + assertNotNull(document.toString().contains(VALUE)); + } + +} diff --git a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java new file mode 100644 index 000000000..787ae3aa6 --- /dev/null +++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractdocument; + +import org.junit.Test; + +/** + * Simple App test + */ +public class AppTest { + + @Test + public void shouldExecuteAppWithoutException() { + App.main(null); + } + +} diff --git a/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java b/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java new file mode 100644 index 000000000..437244a3d --- /dev/null +++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java @@ -0,0 +1,77 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractdocument; + +import com.iluwatar.abstractdocument.domain.Car; +import com.iluwatar.abstractdocument.domain.HasModel; +import com.iluwatar.abstractdocument.domain.HasParts; +import com.iluwatar.abstractdocument.domain.HasPrice; +import com.iluwatar.abstractdocument.domain.HasType; +import com.iluwatar.abstractdocument.domain.Part; +import org.junit.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import static junit.framework.TestCase.assertEquals; + +/** + * Test for Part and Car + */ +public class DomainTest { + + private static final String TEST_PART_TYPE = "test-part-type"; + private static final String TEST_PART_MODEL = "test-part-model"; + private static final long TEST_PART_PRICE = 0L; + + private static final String TEST_CAR_MODEL = "test-car-model"; + private static final long TEST_CAR_PRICE = 1L; + + @Test + public void shouldConstructPart() { + Map partProperties = new HashMap<>(); + partProperties.put(HasType.PROPERTY, TEST_PART_TYPE); + partProperties.put(HasModel.PROPERTY, TEST_PART_MODEL); + partProperties.put(HasPrice.PROPERTY, TEST_PART_PRICE); + Part part = new Part(partProperties); + + assertEquals(TEST_PART_TYPE, part.getType().get()); + assertEquals(TEST_PART_MODEL, part.getModel().get()); + assertEquals(TEST_PART_PRICE, part.getPrice().get()); + } + + @Test + public void shouldConstructCar() { + Map carProperties = new HashMap<>(); + carProperties.put(HasModel.PROPERTY, TEST_CAR_MODEL); + carProperties.put(HasPrice.PROPERTY, TEST_CAR_PRICE); + carProperties.put(HasParts.PROPERTY, Arrays.asList(new HashMap<>(), new HashMap<>())); + Car car = new Car(carProperties); + + assertEquals(TEST_CAR_MODEL, car.getModel().get()); + assertEquals(TEST_CAR_PRICE, car.getPrice().get()); + assertEquals(2, car.getParts().count()); + } + +} diff --git a/abstract-factory/README.md b/abstract-factory/README.md new file mode 100644 index 000000000..485599b98 --- /dev/null +++ b/abstract-factory/README.md @@ -0,0 +1,36 @@ +--- +layout: pattern +title: Abstract Factory +folder: abstract-factory +permalink: /patterns/abstract-factory/ +categories: Creational +tags: + - Java + - Gang Of Four + - Difficulty-Intermediate +--- + +## Also known as +Kit + +## Intent +Provide an interface for creating families of related or dependent +objects without specifying their concrete classes. + +![alt text](./etc/abstract-factory_1.png "Abstract Factory") + +## Applicability +Use the Abstract Factory pattern when + +* a system should be independent of how its products are created, composed and represented +* a system should be configured with one of multiple families of products +* a family of related product objects is designed to be used together, and you need to enforce this constraint +* you want to provide a class library of products, and you want to reveal just their interfaces, not their implementations + +## Real world examples + +* [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html) + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/abstract-factory/pom.xml b/abstract-factory/pom.xml index a071aee26..ec0f700a5 100644 --- a/abstract-factory/pom.xml +++ b/abstract-factory/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT abstract-factory diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java index 3f77a521c..aae396f1d 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java @@ -1,29 +1,115 @@ -package com.iluwatar.abstractfactory; - -/** - * - * The essence of the Abstract Factory pattern is a factory interface - * (KingdomFactory) and its implementations (ElfKingdomFactory, - * OrcKingdomFactory). - * - * The example uses both concrete implementations to create a king, a castle and - * an army. - * - */ -public class App { - - public static void main(String[] args) { - createKingdom(new ElfKingdomFactory()); - createKingdom(new OrcKingdomFactory()); - } - - public static void createKingdom(KingdomFactory factory) { - King king = factory.createKing(); - Castle castle = factory.createCastle(); - Army army = factory.createArmy(); - System.out.println("The kingdom was created."); - System.out.println(king); - System.out.println(castle); - System.out.println(army); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractfactory; + +/** + * + * The Abstract Factory pattern provides a way to encapsulate a group of individual factories that have a common theme + * without specifying their concrete classes. In normal usage, the client software creates a concrete implementation of + * the abstract factory and then uses the generic interface of the factory to create the concrete objects that are part + * of the theme. The client does not know (or care) which concrete objects it gets from each of these internal + * factories, since it uses only the generic interfaces of their products. This pattern separates the details of + * implementation of a set of objects from their general usage and relies on object composition, as object creation is + * implemented in methods exposed in the factory interface. + *

+ * The essence of the Abstract Factory pattern is a factory interface ({@link KingdomFactory}) and its implementations ( + * {@link ElfKingdomFactory}, {@link OrcKingdomFactory}). The example uses both concrete implementations to create a + * king, a castle and an army. + * + */ +public class App { + + private King king; + private Castle castle; + private Army army; + + /** + * Creates kingdom + */ + public void createKingdom(final KingdomFactory factory) { + setKing(factory.createKing()); + setCastle(factory.createCastle()); + setArmy(factory.createArmy()); + } + + King getKing(final KingdomFactory factory) { + return factory.createKing(); + } + + public King getKing() { + return king; + } + + private void setKing(final King king) { + this.king = king; + } + + Castle getCastle(final KingdomFactory factory) { + return factory.createCastle(); + } + + public Castle getCastle() { + return castle; + } + + private void setCastle(final Castle castle) { + this.castle = castle; + } + + Army getArmy(final KingdomFactory factory) { + return factory.createArmy(); + } + + public Army getArmy() { + return army; + } + + private void setArmy(final Army army) { + this.army = army; + } + + /** + * Program entry point + * + * @param args + * command line args + */ + public static void main(String[] args) { + + App app = new App(); + + System.out.println("Elf Kingdom"); + app.createKingdom(new ElfKingdomFactory()); + System.out.println(app.getArmy().getDescription()); + System.out.println(app.getCastle().getDescription()); + System.out.println(app.getKing().getDescription()); + + System.out.println("\nOrc Kingdom"); + app.createKingdom(new OrcKingdomFactory()); + System.out.println(app.getArmy().getDescription()); + System.out.println(app.getCastle().getDescription()); + System.out.println(app.getKing().getDescription()); + + } + +} diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Army.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Army.java index 39e023e3b..d9e7f9989 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Army.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Army.java @@ -1,5 +1,33 @@ -package com.iluwatar.abstractfactory; - -public interface Army { - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractfactory; + +/** + * + * Army interface + * + */ +public interface Army { + + String getDescription(); +} diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Castle.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Castle.java index 277daea56..adea2327e 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Castle.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Castle.java @@ -1,5 +1,33 @@ -package com.iluwatar.abstractfactory; - -public interface Castle { - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractfactory; + +/** + * + * Castle interface + * + */ +public interface Castle { + + String getDescription(); +} diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfArmy.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfArmy.java index 473106222..2969a8615 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfArmy.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfArmy.java @@ -1,10 +1,38 @@ -package com.iluwatar.abstractfactory; - -public class ElfArmy implements Army { - - @Override - public String toString() { - return "This is the Elven Army!"; - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractfactory; + +/** + * + * ElfArmy + * + */ +public class ElfArmy implements Army { + + static final String DESCRIPTION = "This is the Elven Army!"; + + @Override + public String getDescription() { + return DESCRIPTION; + } +} diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfCastle.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfCastle.java index 851baf84f..5321bfeba 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfCastle.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfCastle.java @@ -1,10 +1,38 @@ -package com.iluwatar.abstractfactory; - -public class ElfCastle implements Castle { - - @Override - public String toString() { - return "This is the Elven castle!"; - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractfactory; + +/** + * + * ElfCastle + * + */ +public class ElfCastle implements Castle { + + static final String DESCRIPTION = "This is the Elven castle!"; + + @Override + public String getDescription() { + return DESCRIPTION; + } +} diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKing.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKing.java index eafaccf49..1eb892e6f 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKing.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKing.java @@ -1,10 +1,38 @@ -package com.iluwatar.abstractfactory; - -public class ElfKing implements King { - - @Override - public String toString() { - return "This is the Elven king!"; - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractfactory; + +/** + * + * ElfKing + * + */ +public class ElfKing implements King { + + static final String DESCRIPTION = "This is the Elven king!"; + + @Override + public String getDescription() { + return DESCRIPTION; + } +} diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKingdomFactory.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKingdomFactory.java index eb4b99685..9d48ab25f 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKingdomFactory.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKingdomFactory.java @@ -1,22 +1,44 @@ -package com.iluwatar.abstractfactory; - -/** - * - * Concrete factory. - * - */ -public class ElfKingdomFactory implements KingdomFactory { - - public Castle createCastle() { - return new ElfCastle(); - } - - public King createKing() { - return new ElfKing(); - } - - public Army createArmy() { - return new ElfArmy(); - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractfactory; + +/** + * + * ElfKingdomFactory concrete factory. + * + */ +public class ElfKingdomFactory implements KingdomFactory { + + public Castle createCastle() { + return new ElfCastle(); + } + + public King createKing() { + return new ElfKing(); + } + + public Army createArmy() { + return new ElfArmy(); + } + +} diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/King.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/King.java index 12a9c1f13..ec1cff4d1 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/King.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/King.java @@ -1,5 +1,33 @@ -package com.iluwatar.abstractfactory; - -public interface King { - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractfactory; + +/** + * + * King interface + * + */ +public interface King { + + String getDescription(); +} diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/KingdomFactory.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/KingdomFactory.java index e5b4b4050..d8258fd8b 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/KingdomFactory.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/KingdomFactory.java @@ -1,16 +1,38 @@ -package com.iluwatar.abstractfactory; - -/** - * - * The factory interface. - * - */ -public interface KingdomFactory { - - Castle createCastle(); - - King createKing(); - - Army createArmy(); - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractfactory; + +/** + * + * KingdomFactory factory interface. + * + */ +public interface KingdomFactory { + + Castle createCastle(); + + King createKing(); + + Army createArmy(); + +} diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcArmy.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcArmy.java index b0e202d51..261ad37c9 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcArmy.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcArmy.java @@ -1,10 +1,38 @@ -package com.iluwatar.abstractfactory; - -public class OrcArmy implements Army { - - @Override - public String toString() { - return "This is the Orcish Army!"; - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractfactory; + +/** + * + * OrcArmy + * + */ +public class OrcArmy implements Army { + + static final String DESCRIPTION = "This is the Orc Army!"; + + @Override + public String getDescription() { + return DESCRIPTION; + } +} diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcCastle.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcCastle.java index 785884a59..cb2a92652 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcCastle.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcCastle.java @@ -1,10 +1,38 @@ -package com.iluwatar.abstractfactory; - -public class OrcCastle implements Castle { - - @Override - public String toString() { - return "This is the Orcish castle!"; - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractfactory; + +/** + * + * OrcCastle + * + */ +public class OrcCastle implements Castle { + + static final String DESCRIPTION = "This is the Orc castle!"; + + @Override + public String getDescription() { + return DESCRIPTION; + } +} diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKing.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKing.java index 27ea8afd4..ba7576492 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKing.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKing.java @@ -1,10 +1,38 @@ -package com.iluwatar.abstractfactory; - -public class OrcKing implements King { - - @Override - public String toString() { - return "This is the Orc king!"; - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractfactory; + +/** + * + * OrcKing + * + */ +public class OrcKing implements King { + + static final String DESCRIPTION = "This is the Orc king!"; + + @Override + public String getDescription() { + return DESCRIPTION; + } +} diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKingdomFactory.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKingdomFactory.java index 2f2a2a54d..2d740cf0d 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKingdomFactory.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKingdomFactory.java @@ -1,22 +1,43 @@ -package com.iluwatar.abstractfactory; - -/** - * - * Concrete factory. - * - */ -public class OrcKingdomFactory implements KingdomFactory { - - public Castle createCastle() { - return new OrcCastle(); - } - - public King createKing() { - return new OrcKing(); - } - - public Army createArmy() { - return new OrcArmy(); - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractfactory; + +/** + * + * OrcKingdomFactory concrete factory. + * + */ +public class OrcKingdomFactory implements KingdomFactory { + + public Castle createCastle() { + return new OrcCastle(); + } + + public King createKing() { + return new OrcKing(); + } + + public Army createArmy() { + return new OrcArmy(); + } +} diff --git a/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java new file mode 100644 index 000000000..216f0443a --- /dev/null +++ b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java @@ -0,0 +1,100 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractfactory; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Before; +import org.junit.Test; + +public class AbstractFactoryTest { + + private App app = new App(); + private KingdomFactory elfFactory; + private KingdomFactory orcFactory; + + @Before + public void setUp() { + elfFactory = new ElfKingdomFactory(); + orcFactory = new OrcKingdomFactory(); + } + + @Test + public void king() { + final King elfKing = app.getKing(elfFactory); + assertTrue(elfKing instanceof ElfKing); + assertEquals(ElfKing.DESCRIPTION, elfKing.getDescription()); + final King orcKing = app.getKing(orcFactory); + assertTrue(orcKing instanceof OrcKing); + assertEquals(OrcKing.DESCRIPTION, orcKing.getDescription()); + } + + @Test + public void castle() { + final Castle elfCastle = app.getCastle(elfFactory); + assertTrue(elfCastle instanceof ElfCastle); + assertEquals(ElfCastle.DESCRIPTION, elfCastle.getDescription()); + final Castle orcCastle = app.getCastle(orcFactory); + assertTrue(orcCastle instanceof OrcCastle); + assertEquals(OrcCastle.DESCRIPTION, orcCastle.getDescription()); + } + + @Test + public void army() { + final Army elfArmy = app.getArmy(elfFactory); + assertTrue(elfArmy instanceof ElfArmy); + assertEquals(ElfArmy.DESCRIPTION, elfArmy.getDescription()); + final Army orcArmy = app.getArmy(orcFactory); + assertTrue(orcArmy instanceof OrcArmy); + assertEquals(OrcArmy.DESCRIPTION, orcArmy.getDescription()); + } + + @Test + public void createElfKingdom() { + app.createKingdom(elfFactory); + final King king = app.getKing(); + final Castle castle = app.getCastle(); + final Army army = app.getArmy(); + assertTrue(king instanceof ElfKing); + assertEquals(ElfKing.DESCRIPTION, king.getDescription()); + assertTrue(castle instanceof ElfCastle); + assertEquals(ElfCastle.DESCRIPTION, castle.getDescription()); + assertTrue(army instanceof ElfArmy); + assertEquals(ElfArmy.DESCRIPTION, army.getDescription()); + } + + @Test + public void createOrcKingdom() { + app.createKingdom(orcFactory); + final King king = app.getKing(); + final Castle castle = app.getCastle(); + final Army army = app.getArmy(); + assertTrue(king instanceof OrcKing); + assertEquals(OrcKing.DESCRIPTION, king.getDescription()); + assertTrue(castle instanceof OrcCastle); + assertEquals(OrcCastle.DESCRIPTION, castle.getDescription()); + assertTrue(army instanceof OrcArmy); + assertEquals(OrcArmy.DESCRIPTION, army.getDescription()); + } +} diff --git a/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java index 0241ed8ce..a965284f7 100644 --- a/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java +++ b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java @@ -1,13 +1,38 @@ -package com.iluwatar.abstractfactory; -import org.junit.Test; - -import com.iluwatar.abstractfactory.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractfactory; + +import org.junit.Test; + +import java.io.IOException; + +/** + * Tests that Abstract Factory example runs without errors. + */ +public class AppTest { + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } +} diff --git a/adapter/README.md b/adapter/README.md new file mode 100644 index 000000000..ea3baa7fa --- /dev/null +++ b/adapter/README.md @@ -0,0 +1,37 @@ +--- +layout: pattern +title: Adapter +folder: adapter +permalink: /patterns/adapter/ +categories: Structural +tags: + - Java + - Gang Of Four + - Difficulty-Beginner +--- + +## Also known as +Wrapper + +## Intent +Convert the interface of a class into another interface the clients +expect. Adapter lets classes work together that couldn't otherwise because of +incompatible interfaces. + +![alt text](./etc/adapter.png "Adapter") + +## Applicability +Use the Adapter pattern when + +* you want to use an existing class, and its interface does not match the one you need +* you want to create a reusable class that cooperates with unrelated or unforeseen classes, that is, classes that don't necessarily have compatible interfaces +* you need to use several existing subclasses, but it's impractical to adapt their interface by subclassing every one. An object adapter can adapt the interface of its parent class. + +## Real world examples + +* [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29) + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) +* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) diff --git a/adapter/etc/adapter.png b/adapter/etc/adapter.png index 511bb5880..f43358b04 100644 Binary files a/adapter/etc/adapter.png and b/adapter/etc/adapter.png differ diff --git a/adapter/etc/adapter.ucls b/adapter/etc/adapter.ucls index 8c09f0399..290ff544e 100644 --- a/adapter/etc/adapter.ucls +++ b/adapter/etc/adapter.ucls @@ -1,61 +1,61 @@ - - - + + + - - + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - + + - - - - - - - - + + + + - + + + + + - - + + diff --git a/adapter/etc/adapter_1.png b/adapter/etc/adapter_1.png deleted file mode 100644 index 64eb34b84..000000000 Binary files a/adapter/etc/adapter_1.png and /dev/null differ diff --git a/adapter/pom.xml b/adapter/pom.xml index 7a6861fca..2c99796f4 100644 --- a/adapter/pom.xml +++ b/adapter/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT adapter @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/adapter/src/main/java/com/iluwatar/adapter/App.java b/adapter/src/main/java/com/iluwatar/adapter/App.java index 43a93fa44..29c313b59 100644 --- a/adapter/src/main/java/com/iluwatar/adapter/App.java +++ b/adapter/src/main/java/com/iluwatar/adapter/App.java @@ -1,21 +1,60 @@ -package com.iluwatar.adapter; - -/** - * - * There are two variations of the Adapter pattern: The class adapter implements - * the adaptee's interface whereas the object adapter uses composition to - * contain the adaptee in the adapter object. This example uses the object - * adapter approach. - * - * The Adapter (GnomeEngineer) converts the interface of the target class - * (GoblinGlider) into a suitable one expected by the client - * (GnomeEngineeringManager). - * - */ -public class App { - - public static void main(String[] args) { - Engineer manager = new GnomeEngineeringManager(); - manager.operateDevice(); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.adapter; + +/** + * An adapter helps two incompatible interfaces to work together. This is the real world definition + * for an adapter. Interfaces may be incompatible but the inner functionality should suit the need. + * The Adapter design pattern allows otherwise incompatible classes to work together by converting + * the interface of one class into an interface expected by the clients. + * + *

+ * There are two variations of the Adapter pattern: The class adapter implements the adaptee's + * interface whereas the object adapter uses composition to contain the adaptee in the adapter + * object. This example uses the object adapter approach. + * + *

+ * The Adapter ({@link BattleFishingBoat}) converts the interface of the adaptee class ( + * {@link FishingBoat}) into a suitable one expected by the client ( {@link BattleShip} ). + * + *

+ * The story of this implementation is this.
+ * Pirates are coming! we need a {@link BattleShip} to fight! We have a {@link FishingBoat} and our + * captain. We have no time to make up a new ship! we need to reuse this {@link FishingBoat}. The + * captain needs a battleship which can fire and move. The spec is in {@link BattleShip}. We will + * use the Adapter pattern to reuse {@link FishingBoat}. + * + */ +public class App { + + /** + * Program entry point. + * + * @param args command line args + */ + public static void main(String[] args) { + Captain captain = new Captain(new BattleFishingBoat()); + captain.move(); + captain.fire(); + } +} diff --git a/adapter/src/main/java/com/iluwatar/adapter/BattleFishingBoat.java b/adapter/src/main/java/com/iluwatar/adapter/BattleFishingBoat.java new file mode 100644 index 000000000..a591818fe --- /dev/null +++ b/adapter/src/main/java/com/iluwatar/adapter/BattleFishingBoat.java @@ -0,0 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.adapter; + +/** + * + * Adapter class. Adapts the interface of the device ({@link FishingBoat}) into {@link BattleShip} + * interface expected by the client ({@link Captain}).
+ * In this case we added a new function fire to suit the interface. We are reusing the + * {@link FishingBoat} without changing itself. The Adapter class can just map the functions of the + * Adaptee or add, delete features of the Adaptee. + * + */ +public class BattleFishingBoat implements BattleShip { + + private FishingBoat boat; + + public BattleFishingBoat() { + boat = new FishingBoat(); + } + + @Override + public void fire() { + System.out.println("fire!"); + } + + @Override + public void move() { + boat.sail(); + } +} diff --git a/adapter/src/main/java/com/iluwatar/adapter/BattleShip.java b/adapter/src/main/java/com/iluwatar/adapter/BattleShip.java new file mode 100644 index 000000000..6a29a5034 --- /dev/null +++ b/adapter/src/main/java/com/iluwatar/adapter/BattleShip.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.adapter; + +/** + * The interface expected by the client.
+ * A Battleship can fire and move. + * + */ +public interface BattleShip { + + void fire(); + + void move(); + +} diff --git a/adapter/src/main/java/com/iluwatar/adapter/Captain.java b/adapter/src/main/java/com/iluwatar/adapter/Captain.java new file mode 100644 index 000000000..34f783cd4 --- /dev/null +++ b/adapter/src/main/java/com/iluwatar/adapter/Captain.java @@ -0,0 +1,55 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.adapter; + +/** + * The Captain uses {@link BattleShip} to fight.
+ * This is the client in the pattern. + */ +public class Captain implements BattleShip { + + private BattleShip battleship; + + public Captain() { + + } + + public Captain(BattleShip battleship) { + this.battleship = battleship; + } + + public void setBattleship(BattleShip battleship) { + this.battleship = battleship; + } + + @Override + public void fire() { + battleship.fire(); + } + + @Override + public void move() { + battleship.move(); + } + +} diff --git a/adapter/src/main/java/com/iluwatar/adapter/Engineer.java b/adapter/src/main/java/com/iluwatar/adapter/Engineer.java deleted file mode 100644 index 7478b5b69..000000000 --- a/adapter/src/main/java/com/iluwatar/adapter/Engineer.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.iluwatar.adapter; - -/** - * - * Engineers can operate devices. - * - */ -public interface Engineer { - - void operateDevice(); - -} diff --git a/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java b/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java new file mode 100644 index 000000000..307437038 --- /dev/null +++ b/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.adapter; + +/** + * + * Device class (adaptee in the pattern). We want to reuse this class + * + */ +public class FishingBoat { + + public void sail() { + System.out.println("The Boat is moving to that place"); + } + + public void fish() { + System.out.println("fishing ..."); + } + +} diff --git a/adapter/src/main/java/com/iluwatar/adapter/GnomeEngineer.java b/adapter/src/main/java/com/iluwatar/adapter/GnomeEngineer.java deleted file mode 100644 index 62fab599f..000000000 --- a/adapter/src/main/java/com/iluwatar/adapter/GnomeEngineer.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.iluwatar.adapter; - -/** - * - * Adapter class. Adapts the interface of the device (GoblinGlider) into - * Engineer interface expected by the client (GnomeEngineeringManager). - * - */ -public class GnomeEngineer implements Engineer { - - private GoblinGlider glider; - - public GnomeEngineer() { - glider = new GoblinGlider(); - } - - @Override - public void operateDevice() { - glider.attachGlider(); - glider.gainSpeed(); - glider.takeOff(); - } - -} diff --git a/adapter/src/main/java/com/iluwatar/adapter/GnomeEngineeringManager.java b/adapter/src/main/java/com/iluwatar/adapter/GnomeEngineeringManager.java deleted file mode 100644 index 6d0010d74..000000000 --- a/adapter/src/main/java/com/iluwatar/adapter/GnomeEngineeringManager.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.iluwatar.adapter; - -/** - * - * GnomeEngineering manager uses Engineer to operate devices. - * - */ -public class GnomeEngineeringManager implements Engineer { - - private Engineer engineer; - - public GnomeEngineeringManager() { - engineer = new GnomeEngineer(); - } - - @Override - public void operateDevice() { - engineer.operateDevice(); - } -} diff --git a/adapter/src/main/java/com/iluwatar/adapter/GoblinGlider.java b/adapter/src/main/java/com/iluwatar/adapter/GoblinGlider.java deleted file mode 100644 index ff1dbeb8d..000000000 --- a/adapter/src/main/java/com/iluwatar/adapter/GoblinGlider.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.iluwatar.adapter; - -/** - * - * Device class (adaptee in the pattern). - * - */ -public class GoblinGlider { - - public void attachGlider() { - System.out.println("Glider attached."); - } - - public void gainSpeed() { - System.out.println("Gaining speed."); - } - - public void takeOff() { - System.out.println("Lift-off!"); - } -} diff --git a/adapter/src/test/java/com/iluwatar/adapter/AdapterPatternTest.java b/adapter/src/test/java/com/iluwatar/adapter/AdapterPatternTest.java new file mode 100644 index 000000000..263c9ab02 --- /dev/null +++ b/adapter/src/test/java/com/iluwatar/adapter/AdapterPatternTest.java @@ -0,0 +1,83 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.adapter; + +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +/** + * Test class + * + */ +public class AdapterPatternTest { + + private Map beans; + + private static final String BATTLESHIP_BEAN = "engineer"; + + private static final String CAPTAIN_BEAN = "captain"; + + /** + * This method runs before the test execution and sets the bean objects in the beans Map. + */ + @Before + public void setup() { + beans = new HashMap<>(); + + BattleFishingBoat battleFishingBoat = spy(new BattleFishingBoat()); + beans.put(BATTLESHIP_BEAN, battleFishingBoat); + + Captain captain = new Captain(); + captain.setBattleship((BattleFishingBoat) beans.get(BATTLESHIP_BEAN)); + beans.put(CAPTAIN_BEAN, captain); + } + + /** + * This test asserts that when we use the move() method on a captain bean(client), it is + * internally calling move method on the battleship object. The Adapter ({@link BattleFishingBoat} + * ) converts the interface of the target class ( {@link FishingBoat}) into a suitable one + * expected by the client ({@link Captain} ). + */ + @Test + public void testAdapter() { + BattleShip captain = (BattleShip) beans.get(CAPTAIN_BEAN); + + // when captain moves + captain.move(); + + // the captain internally calls the battleship object to move + BattleShip battleship = (BattleShip) beans.get(BATTLESHIP_BEAN); + verify(battleship).move(); + + // same with above with firing + captain.fire(); + verify(battleship).fire(); + + } +} diff --git a/adapter/src/test/java/com/iluwatar/adapter/AppTest.java b/adapter/src/test/java/com/iluwatar/adapter/AppTest.java index cd9d713b0..4f5fcd91d 100644 --- a/adapter/src/test/java/com/iluwatar/adapter/AppTest.java +++ b/adapter/src/test/java/com/iluwatar/adapter/AppTest.java @@ -1,14 +1,38 @@ -package com.iluwatar.adapter; - -import org.junit.Test; - -import com.iluwatar.adapter.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.adapter; + +import org.junit.Test; + +import java.io.IOException; + +/** + * Tests that Adapter example runs without errors. + */ +public class AppTest { + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } +} diff --git a/aggregator-microservices/README.md b/aggregator-microservices/README.md new file mode 100644 index 000000000..e65f26d9a --- /dev/null +++ b/aggregator-microservices/README.md @@ -0,0 +1,29 @@ +--- +layout: pattern +title: Aggregator Microservices +folder: aggregator-microservices +permalink: /patterns/aggregator-microservices/ +categories: Architectural +tags: +- Java +- Spring +--- + +## Intent + +The user makes a single call to the Aggregator, and the aggregator then calls each relevant microservice and collects +the data, apply business logic to it, and further publish is as a REST Endpoint. +More variations of the aggregator are: +- Proxy Microservice Design Pattern: A different microservice is called upon the business need. +- Chained Microservice Design Pattern: In this case each microservice is dependent/ chained to a series +of other microservices. + +![alt text](./etc/aggregator-microservice.png "Aggregator Microservice") + +## Applicability + +Use the Aggregator Microservices pattern when you need a unified API for various microservices, regardless the client device. + +## Credits + +* [Microservice Design Patterns](http://blog.arungupta.me/microservice-design-patterns/) diff --git a/aggregator-microservices/aggregator-service/pom.xml b/aggregator-microservices/aggregator-service/pom.xml new file mode 100644 index 000000000..169d0da94 --- /dev/null +++ b/aggregator-microservices/aggregator-service/pom.xml @@ -0,0 +1,88 @@ + + + + + aggregator-microservices + com.iluwatar + 1.13.0-SNAPSHOT + + 4.0.0 + + aggregator-service + jar + + + + + org.springframework.boot + spring-boot-dependencies + + + + + + org.springframework + spring-webmvc + + + org.springframework.boot + spring-boot-starter-web + + + junit + junit + test + + + org.mockito + mockito-core + test + + + org.apache.httpcomponents + httpclient + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + \ No newline at end of file diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Aggregator.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Aggregator.java new file mode 100644 index 000000000..639349db9 --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Aggregator.java @@ -0,0 +1,58 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.aggregator.microservices; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +/** + * The aggregator aggregates calls on various micro-services, collects + * data and further publishes them under a REST endpoint. + */ +@RestController +public class Aggregator { + + + @Resource + private ProductInformationClient informationClient; + + @Resource + private ProductInventoryClient inventoryClient; + + + /** + * Retrieves product data. + * + * @return a Product. + */ + @RequestMapping("/product") + public Product getProduct() { + Product product = new Product(); + product.setTitle(informationClient.getProductTitle()); + product.setProductInventories(inventoryClient.getProductInventories()); + return product; + } + +} diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/App.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/App.java new file mode 100644 index 000000000..25bb9ee6f --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/App.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.aggregator.microservices; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + SpringApplication.run(App.class, args); + } +} diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Product.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Product.java new file mode 100644 index 000000000..edf0c121d --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Product.java @@ -0,0 +1,57 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.aggregator.microservices; + +/** + * Encapsulates all the data for a Product that clients will request. + */ +public class Product { + + /** + * The title of the product. + */ + private String title; + + + /** + * The inventories of the product. + */ + private int productInventories; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public int getProductInventories() { + return productInventories; + } + + public void setProductInventories(int productInventories) { + this.productInventories = productInventories; + } + +} diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInformationClient.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInformationClient.java new file mode 100644 index 000000000..863db4759 --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInformationClient.java @@ -0,0 +1,32 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.aggregator.microservices; + +/** + * Interface for the Information micro-service. + */ +public interface ProductInformationClient { + + String getProductTitle(); + +} diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInformationClientImpl.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInformationClientImpl.java new file mode 100644 index 000000000..1c5c1527c --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInformationClientImpl.java @@ -0,0 +1,53 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.aggregator.microservices; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +/** + * An adapter to communicate with information micro-service. + */ +@Component +public class ProductInformationClientImpl implements ProductInformationClient { + + @Override + public String getProductTitle() { + String response = null; + try (CloseableHttpClient httpClient = HttpClients.createDefault()) { + HttpGet httpGet = new HttpGet("http://localhost:51515/information"); + try (CloseableHttpResponse httpResponse = httpClient.execute(httpGet)) { + response = EntityUtils.toString(httpResponse.getEntity()); + } + } catch (IOException e) { + e.printStackTrace(); + } + return response; + } +} diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClient.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClient.java new file mode 100644 index 000000000..22be900b3 --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClient.java @@ -0,0 +1,31 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.aggregator.microservices; + +/** + * Interface to Inventory micro-service. + */ +public interface ProductInventoryClient { + + int getProductInventories(); +} diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClientImpl.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClientImpl.java new file mode 100644 index 000000000..14d0a32c4 --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClientImpl.java @@ -0,0 +1,53 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.aggregator.microservices; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +/** + * An adapter to communicate with inventory micro-service. + */ +@Component +public class ProductInventoryClientImpl implements ProductInventoryClient { + + @Override + public int getProductInventories() { + String response = "0"; + try (CloseableHttpClient httpClient = HttpClients.createDefault()) { + HttpGet httpGet = new HttpGet("http://localhost:51516/inventories"); + try (CloseableHttpResponse httpResponse = httpClient.execute(httpGet)) { + response = EntityUtils.toString(httpResponse.getEntity()); + } + } catch (IOException e) { + e.printStackTrace(); + } + return Integer.parseInt(response); + } +} diff --git a/aggregator-microservices/aggregator-service/src/main/resources/application.properties b/aggregator-microservices/aggregator-service/src/main/resources/application.properties new file mode 100644 index 000000000..69f581712 --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/main/resources/application.properties @@ -0,0 +1,24 @@ +# +# The MIT License +# Copyright (c) 2014 Ilkka Seppälä +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +server.port=50004 \ No newline at end of file diff --git a/aggregator-microservices/aggregator-service/src/test/java/com/iluwatar/aggregator/microservices/AggregatorTest.java b/aggregator-microservices/aggregator-service/src/test/java/com/iluwatar/aggregator/microservices/AggregatorTest.java new file mode 100644 index 000000000..95d36fe25 --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/test/java/com/iluwatar/aggregator/microservices/AggregatorTest.java @@ -0,0 +1,67 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.aggregator.microservices; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; + +public class AggregatorTest { + + @InjectMocks + private Aggregator aggregator; + + @Mock + private ProductInformationClient informationClient; + + @Mock + private ProductInventoryClient inventoryClient; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + } + + /** + * Tests getting the data for a desktop client + */ + @Test + public void testGetProduct() { + String title = "The Product Title."; + int inventories = 5; + + when(informationClient.getProductTitle()).thenReturn(title); + when(inventoryClient.getProductInventories()).thenReturn(inventories); + + Product testProduct = aggregator.getProduct(); + + assertEquals(title, testProduct.getTitle()); + assertEquals(inventories, testProduct.getProductInventories()); + } + +} \ No newline at end of file diff --git a/aggregator-microservices/etc/aggregator-microservice.png b/aggregator-microservices/etc/aggregator-microservice.png new file mode 100644 index 000000000..ad344a7e1 Binary files /dev/null and b/aggregator-microservices/etc/aggregator-microservice.png differ diff --git a/aggregator-microservices/information-microservice/pom.xml b/aggregator-microservices/information-microservice/pom.xml new file mode 100644 index 000000000..986540344 --- /dev/null +++ b/aggregator-microservices/information-microservice/pom.xml @@ -0,0 +1,79 @@ + + + + + aggregator-microservices + com.iluwatar + 1.13.0-SNAPSHOT + + 4.0.0 + + information-microservice + jar + + + + + org.springframework.boot + spring-boot-dependencies + + + + + + org.springframework + spring-webmvc + + + org.springframework.boot + spring-boot-starter-web + + + junit + junit + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + diff --git a/aggregator-microservices/information-microservice/src/main/java/com/iluwatar/information/microservice/InformationApplication.java b/aggregator-microservices/information-microservice/src/main/java/com/iluwatar/information/microservice/InformationApplication.java new file mode 100644 index 000000000..c93219c17 --- /dev/null +++ b/aggregator-microservices/information-microservice/src/main/java/com/iluwatar/information/microservice/InformationApplication.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.information.microservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * Inventory Application starts container (Spring Boot) and exposes the Inventory micro-service. + */ +@SpringBootApplication +public class InformationApplication { + + public static void main(String[] args) { + SpringApplication.run(InformationApplication.class, args); + } +} diff --git a/aggregator-microservices/information-microservice/src/main/java/com/iluwatar/information/microservice/InformationController.java b/aggregator-microservices/information-microservice/src/main/java/com/iluwatar/information/microservice/InformationController.java new file mode 100644 index 000000000..37ec45c1b --- /dev/null +++ b/aggregator-microservices/information-microservice/src/main/java/com/iluwatar/information/microservice/InformationController.java @@ -0,0 +1,41 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.information.microservice; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class InformationController { + + /** + * Endpoint to retrieve a product's informations. + * + * @return product inventory. + */ + @RequestMapping(value = "/information", method = RequestMethod.GET) + public String getProductTitle() { + return "The Product Title."; + } +} diff --git a/aggregator-microservices/information-microservice/src/main/resources/application.properties b/aggregator-microservices/information-microservice/src/main/resources/application.properties new file mode 100644 index 000000000..3c8452f1f --- /dev/null +++ b/aggregator-microservices/information-microservice/src/main/resources/application.properties @@ -0,0 +1,24 @@ +# +# The MIT License +# Copyright (c) 2014 Ilkka Seppälä +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +server.port=51515 \ No newline at end of file diff --git a/aggregator-microservices/information-microservice/src/test/java/com/iluwatar/information/microservice/InformationControllerTest.java b/aggregator-microservices/information-microservice/src/test/java/com/iluwatar/information/microservice/InformationControllerTest.java new file mode 100644 index 000000000..9c3a6b98d --- /dev/null +++ b/aggregator-microservices/information-microservice/src/test/java/com/iluwatar/information/microservice/InformationControllerTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.information.microservice; + +import org.junit.Assert; +import org.junit.Test; + +public class InformationControllerTest { + + @Test + public void shouldGetProductTitle() { + InformationController infoController = new InformationController(); + + String title = infoController.getProductTitle(); + + Assert.assertEquals("The Product Title.", title); + } + +} \ No newline at end of file diff --git a/aggregator-microservices/inventory-microservice/pom.xml b/aggregator-microservices/inventory-microservice/pom.xml new file mode 100644 index 000000000..f8844dd39 --- /dev/null +++ b/aggregator-microservices/inventory-microservice/pom.xml @@ -0,0 +1,79 @@ + + + + + aggregator-microservices + com.iluwatar + 1.13.0-SNAPSHOT + + 4.0.0 + + inventory-microservice + jar + + + + + org.springframework.boot + spring-boot-dependencies + + + + + + org.springframework + spring-webmvc + + + org.springframework.boot + spring-boot-starter-web + + + junit + junit + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + \ No newline at end of file diff --git a/aggregator-microservices/inventory-microservice/src/main/java/com/iluwatar/inventory/microservice/InventoryApplication.java b/aggregator-microservices/inventory-microservice/src/main/java/com/iluwatar/inventory/microservice/InventoryApplication.java new file mode 100644 index 000000000..3e2cf9e60 --- /dev/null +++ b/aggregator-microservices/inventory-microservice/src/main/java/com/iluwatar/inventory/microservice/InventoryApplication.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.inventory.microservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * Inventory Application starts container (Spring Boot) and exposes the Inventory micro-service. + */ +@SpringBootApplication +public class InventoryApplication { + + public static void main(String[] args) { + SpringApplication.run(InventoryApplication.class, args); + } + +} diff --git a/aggregator-microservices/inventory-microservice/src/main/java/com/iluwatar/inventory/microservice/InventoryController.java b/aggregator-microservices/inventory-microservice/src/main/java/com/iluwatar/inventory/microservice/InventoryController.java new file mode 100644 index 000000000..483e28bf3 --- /dev/null +++ b/aggregator-microservices/inventory-microservice/src/main/java/com/iluwatar/inventory/microservice/InventoryController.java @@ -0,0 +1,42 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.inventory.microservice; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class InventoryController { + + /** + * Endpoint to retrieve a product's inventories. + * + * @return product inventory. + */ + @RequestMapping(value = "/inventories", method = RequestMethod.GET) + public int getProductInventories() { + return 5; + } + +} diff --git a/aggregator-microservices/inventory-microservice/src/main/resources/application.properties b/aggregator-microservices/inventory-microservice/src/main/resources/application.properties new file mode 100644 index 000000000..36f7589fc --- /dev/null +++ b/aggregator-microservices/inventory-microservice/src/main/resources/application.properties @@ -0,0 +1,24 @@ +# +# The MIT License +# Copyright (c) 2014 Ilkka Seppälä +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +server.port=51516 \ No newline at end of file diff --git a/aggregator-microservices/inventory-microservice/src/test/java/com/iluwatar/inventory/microservice/InventoryControllerTest.java b/aggregator-microservices/inventory-microservice/src/test/java/com/iluwatar/inventory/microservice/InventoryControllerTest.java new file mode 100644 index 000000000..787ec0e61 --- /dev/null +++ b/aggregator-microservices/inventory-microservice/src/test/java/com/iluwatar/inventory/microservice/InventoryControllerTest.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.inventory.microservice; + +import org.junit.Assert; +import org.junit.Test; + +public class InventoryControllerTest { + + @Test + public void testGetProductInventories() throws Exception { + InventoryController inventoryController = new InventoryController(); + + int numberOfInventories = inventoryController.getProductInventories(); + + Assert.assertEquals(5, numberOfInventories); + } +} \ No newline at end of file diff --git a/aggregator-microservices/pom.xml b/aggregator-microservices/pom.xml new file mode 100644 index 000000000..0133e9ea4 --- /dev/null +++ b/aggregator-microservices/pom.xml @@ -0,0 +1,43 @@ + + + + + java-design-patterns + com.iluwatar + 1.13.0-SNAPSHOT + + 4.0.0 + aggregator-microservices + pom + + + information-microservice + aggregator-service + inventory-microservice + + \ No newline at end of file diff --git a/api-gateway/README.md b/api-gateway/README.md new file mode 100644 index 000000000..23014ae0b --- /dev/null +++ b/api-gateway/README.md @@ -0,0 +1,30 @@ +--- +layout: pattern +title: API Gateway +folder: api-gateway +permalink: /patterns/api-gateway/ +categories: Architectural +tags: +- Java +- Difficulty-Intermediate +- Spring +--- + +## Intent + +Aggregate calls to microservices in a single location: the API Gateway. The user makes a single +call to the API Gateway, and the API Gateway then calls each relevant microservice. + +![alt text](./etc/api-gateway.png "API Gateway") + +## Applicability + +Use the API Gateway pattern when + +* you're also using the Microservices pattern and need a single point of aggregation for your +microservice calls + +## Credits + +* [microservices.io - API Gateway](http://microservices.io/patterns/apigateway.html) +* [NGINX - Building Microservices: Using an API Gateway](https://www.nginx.com/blog/building-microservices-using-an-api-gateway/) diff --git a/api-gateway/api-gateway-service/pom.xml b/api-gateway/api-gateway-service/pom.xml new file mode 100644 index 000000000..87a1ebb5e --- /dev/null +++ b/api-gateway/api-gateway-service/pom.xml @@ -0,0 +1,87 @@ + + + + + api-gateway + com.iluwatar + 1.13.0-SNAPSHOT + + 4.0.0 + api-gateway-service + jar + + + + + org.springframework.boot + spring-boot-dependencies + + + + + + org.springframework + spring-webmvc + + + org.springframework.boot + spring-boot-starter-web + + + junit + junit + test + + + org.mockito + mockito-core + test + + + org.apache.httpcomponents + httpclient + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + \ No newline at end of file diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ApiGateway.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ApiGateway.java new file mode 100644 index 000000000..60f0b6d54 --- /dev/null +++ b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ApiGateway.java @@ -0,0 +1,64 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.api.gateway; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +/** + * The ApiGateway aggregates calls to microservices based on the needs of the individual clients. + */ +@RestController +public class ApiGateway { + + @Resource + private ImageClient imageClient; + + @Resource + private PriceClient priceClient; + + /** + * Retrieves product information that desktop clients need + * @return Product information for clients on a desktop + */ + @RequestMapping("/desktop") + public DesktopProduct getProductDesktop() { + DesktopProduct desktopProduct = new DesktopProduct(); + desktopProduct.setImagePath(imageClient.getImagePath()); + desktopProduct.setPrice(priceClient.getPrice()); + return desktopProduct; + } + + /** + * Retrieves product information that mobile clients need + * @return Product information for clients on a mobile device + */ + @RequestMapping("/mobile") + public MobileProduct getProductMobile() { + MobileProduct mobileProduct = new MobileProduct(); + mobileProduct.setPrice(priceClient.getPrice()); + return mobileProduct; + } +} diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/App.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/App.java new file mode 100644 index 000000000..9a644a0f7 --- /dev/null +++ b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/App.java @@ -0,0 +1,66 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.api.gateway; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * With the Microservices pattern, a client may need data from multiple different microservices. + * If the client called each microservice directly, that could contribute to longer load times, + * since the client would have to make a network request for each microservice called. Moreover, + * having the client call each microservice directly ties the client to that microservice - if the + * internal implementations of the microservices change (for example, if two microservices are + * combined sometime in the future) or if the location (host and port) of a microservice changes, + * then every client that makes use of those microservices must be updated. + * + *

+ * The intent of the API Gateway pattern is to alleviate some of these issues. In the API Gateway + * pattern, an additional entity (the API Gateway) is placed between the client and the + * microservices. The job of the API Gateway is to aggregate the calls to the microservices. + * Rather than the client calling each microservice individually, the client calls the API Gateway + * a single time. The API Gateway then calls each of the microservices that the client needs. + * + *

+ * This implementation shows what the API Gateway pattern could look like for an e-commerce site. + * The {@link ApiGateway} makes calls to the Image and Price microservices using the + * {@link ImageClientImpl} and {@link PriceClientImpl} respectively. Customers viewing the site on a + * desktop device can see both price information and an image of a product, so the {@link ApiGateway} + * calls both of the microservices and aggregates the data in the {@link DesktopProduct} model. + * However, mobile users only see price information; they do not see a product image. For mobile + * users, the {@link ApiGateway} only retrieves price information, which it uses to populate the + * {@link MobileProduct}. + */ +@SpringBootApplication +public class App { + + /** + * Program entry point + * + * @param args + * command line args + */ + public static void main(String[] args) { + SpringApplication.run(App.class, args); + } +} diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java new file mode 100644 index 000000000..0a04db402 --- /dev/null +++ b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java @@ -0,0 +1,54 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.api.gateway; + +/** + * Encapsulates all of the information that a desktop client needs to display a product. + */ +public class DesktopProduct { + /** + * The price of the product + */ + private String price; + + /** + * The path to the image of the product + */ + private String imagePath; + + public String getPrice() { + return price; + } + + public void setPrice(String price) { + this.price = price; + } + + public String getImagePath() { + return imagePath; + } + + public void setImagePath(String imagePath) { + this.imagePath = imagePath; + } +} diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClient.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClient.java new file mode 100644 index 000000000..935dab778 --- /dev/null +++ b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClient.java @@ -0,0 +1,30 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.api.gateway; + +/** + * An interface used to communicate with the Image microservice + */ +public interface ImageClient { + String getImagePath(); +} diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java new file mode 100644 index 000000000..64fde1e66 --- /dev/null +++ b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java @@ -0,0 +1,56 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.api.gateway; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +/** + * An adapter to communicate with the Image microservice + */ +@Component +public class ImageClientImpl implements ImageClient{ + /** + * Makes a simple HTTP Get request to the Image microservice + * @return The path to the image + */ + @Override + public String getImagePath() { + String response = null; + try (CloseableHttpClient httpClient = HttpClients.createDefault()) { + HttpGet httpGet = new HttpGet("http://localhost:50005/image-path"); + try (CloseableHttpResponse httpResponse = httpClient.execute(httpGet)) { + response = EntityUtils.toString(httpResponse.getEntity()); + } + } catch (IOException e) { + e.printStackTrace(); + } + return response; + } +} diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/MobileProduct.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/MobileProduct.java new file mode 100644 index 000000000..db358b781 --- /dev/null +++ b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/MobileProduct.java @@ -0,0 +1,41 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.api.gateway; + +/** + * Encapsulates all of the information that mobile client needs to display a product. + */ +public class MobileProduct { + /** + * The price of the product + */ + private String price; + + public String getPrice() { + return price; + } + + public void setPrice(String price) { + this.price = price; + } +} diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClient.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClient.java new file mode 100644 index 000000000..44497b997 --- /dev/null +++ b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClient.java @@ -0,0 +1,30 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.api.gateway; + +/** + * An interface used to communicate with the Price microservice + */ +public interface PriceClient { + String getPrice(); +} diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java new file mode 100644 index 000000000..bdc7cb00b --- /dev/null +++ b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java @@ -0,0 +1,56 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.api.gateway; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +/** + * An adapter to communicate with the Price microservice + */ +@Component +public class PriceClientImpl implements PriceClient{ + /** + * Makes a simple HTTP Get request to the Price microservice + * @return The price of the product + */ + @Override + public String getPrice() { + String response = null; + try (CloseableHttpClient httpClient = HttpClients.createDefault()) { + HttpGet httpGet = new HttpGet("http://localhost:50006/price"); + try (CloseableHttpResponse httpResponse = httpClient.execute(httpGet)) { + response = EntityUtils.toString(httpResponse.getEntity()); + } + } catch (IOException e) { + e.printStackTrace(); + } + return response; + } +} diff --git a/api-gateway/api-gateway-service/src/main/resources/application.properties b/api-gateway/api-gateway-service/src/main/resources/application.properties new file mode 100644 index 000000000..69f581712 --- /dev/null +++ b/api-gateway/api-gateway-service/src/main/resources/application.properties @@ -0,0 +1,24 @@ +# +# The MIT License +# Copyright (c) 2014 Ilkka Seppälä +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +server.port=50004 \ No newline at end of file diff --git a/api-gateway/api-gateway-service/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java b/api-gateway/api-gateway-service/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java new file mode 100644 index 000000000..d1a210f05 --- /dev/null +++ b/api-gateway/api-gateway-service/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java @@ -0,0 +1,78 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.api.gateway; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; + +public class ApiGatewayTest { + + @InjectMocks + private ApiGateway apiGateway; + + @Mock + private ImageClient imageClient; + + @Mock + private PriceClient priceClient; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + } + + /** + * Tests getting the data for a desktop client + */ + @Test + public void testGetProductDesktop() { + String imagePath = "/product-image.png"; + String price = "20"; + when(imageClient.getImagePath()).thenReturn(imagePath); + when(priceClient.getPrice()).thenReturn(price); + + DesktopProduct desktopProduct = apiGateway.getProductDesktop(); + + assertEquals(price, desktopProduct.getPrice()); + assertEquals(imagePath, desktopProduct.getImagePath()); + } + + /** + * Tests getting the data for a mobile client + */ + @Test + public void testGetProductMobile() { + String price = "20"; + when(priceClient.getPrice()).thenReturn(price); + + MobileProduct mobileProduct = apiGateway.getProductMobile(); + + assertEquals(price, mobileProduct.getPrice()); + } +} diff --git a/api-gateway/etc/api-gateway.png b/api-gateway/etc/api-gateway.png new file mode 100644 index 000000000..bb3ec2e2e Binary files /dev/null and b/api-gateway/etc/api-gateway.png differ diff --git a/api-gateway/etc/api-gateway.ucls b/api-gateway/etc/api-gateway.ucls new file mode 100644 index 000000000..4a74c2108 --- /dev/null +++ b/api-gateway/etc/api-gateway.ucls @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api-gateway/image-microservice/etc/image-microservice.png b/api-gateway/image-microservice/etc/image-microservice.png new file mode 100644 index 000000000..340285a14 Binary files /dev/null and b/api-gateway/image-microservice/etc/image-microservice.png differ diff --git a/api-gateway/image-microservice/etc/image-microservice.ucls b/api-gateway/image-microservice/etc/image-microservice.ucls new file mode 100644 index 000000000..d3520768f --- /dev/null +++ b/api-gateway/image-microservice/etc/image-microservice.ucls @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api-gateway/image-microservice/pom.xml b/api-gateway/image-microservice/pom.xml new file mode 100644 index 000000000..c29932fae --- /dev/null +++ b/api-gateway/image-microservice/pom.xml @@ -0,0 +1,79 @@ + + + + + api-gateway + com.iluwatar + 1.13.0-SNAPSHOT + + + 4.0.0 + image-microservice + jar + + + + + org.springframework.boot + spring-boot-dependencies + + + + + + org.springframework + spring-webmvc + + + org.springframework.boot + spring-boot-starter-web + + + junit + junit + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + \ No newline at end of file diff --git a/api-gateway/image-microservice/src/main/java/com/iluwatar/image/microservice/ImageApplication.java b/api-gateway/image-microservice/src/main/java/com/iluwatar/image/microservice/ImageApplication.java new file mode 100644 index 000000000..b4cb3d250 --- /dev/null +++ b/api-gateway/image-microservice/src/main/java/com/iluwatar/image/microservice/ImageApplication.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.image.microservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * ImageApplication starts up Spring Boot, exposing endpoints for the Image microservice through + * the {@link ImageController}. + */ +@SpringBootApplication +public class ImageApplication { + + /** + * Microservice entry point + * @param args + * command line args + */ + public static void main(String[] args) { + SpringApplication.run(ImageApplication.class, args); + } +} diff --git a/api-gateway/image-microservice/src/main/java/com/iluwatar/image/microservice/ImageController.java b/api-gateway/image-microservice/src/main/java/com/iluwatar/image/microservice/ImageController.java new file mode 100644 index 000000000..16faf5b70 --- /dev/null +++ b/api-gateway/image-microservice/src/main/java/com/iluwatar/image/microservice/ImageController.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.image.microservice; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +/** + * Exposes the Image microservice's endpoints + */ +@RestController +public class ImageController { + + /** + * An endpoint for a user to retrieve an image path + * @return An image path + */ + @RequestMapping(value = "/image-path", method = RequestMethod.GET) + public String getImagePath() { + return "/product-image.png"; + } +} diff --git a/api-gateway/image-microservice/src/main/resources/application.properties b/api-gateway/image-microservice/src/main/resources/application.properties new file mode 100644 index 000000000..aeda0c24c --- /dev/null +++ b/api-gateway/image-microservice/src/main/resources/application.properties @@ -0,0 +1,24 @@ +# +# The MIT License +# Copyright (c) 2014 Ilkka Seppälä +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +server.port=50005 \ No newline at end of file diff --git a/api-gateway/image-microservice/src/test/java/com/iluwatar/image/microservice/ImageControllerTest.java b/api-gateway/image-microservice/src/test/java/com/iluwatar/image/microservice/ImageControllerTest.java new file mode 100644 index 000000000..cf0c099c1 --- /dev/null +++ b/api-gateway/image-microservice/src/test/java/com/iluwatar/image/microservice/ImageControllerTest.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.image.microservice; + +import org.junit.Assert; +import org.junit.Test; + +public class ImageControllerTest { + @Test + public void testGetImagePath() { + ImageController imageController = new ImageController(); + + String imagePath = imageController.getImagePath(); + + Assert.assertEquals("/product-image.png", imagePath); + } +} diff --git a/api-gateway/pom.xml b/api-gateway/pom.xml new file mode 100644 index 000000000..471ffda7d --- /dev/null +++ b/api-gateway/pom.xml @@ -0,0 +1,43 @@ + + + + + java-design-patterns + com.iluwatar + 1.13.0-SNAPSHOT + + 4.0.0 + api-gateway + pom + + + image-microservice + price-microservice + api-gateway-service + + \ No newline at end of file diff --git a/api-gateway/price-microservice/etc/price-microservice.png b/api-gateway/price-microservice/etc/price-microservice.png new file mode 100644 index 000000000..7c5716025 Binary files /dev/null and b/api-gateway/price-microservice/etc/price-microservice.png differ diff --git a/api-gateway/price-microservice/etc/price-microservice.ucls b/api-gateway/price-microservice/etc/price-microservice.ucls new file mode 100644 index 000000000..5878395b6 --- /dev/null +++ b/api-gateway/price-microservice/etc/price-microservice.ucls @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api-gateway/price-microservice/pom.xml b/api-gateway/price-microservice/pom.xml new file mode 100644 index 000000000..24e9663aa --- /dev/null +++ b/api-gateway/price-microservice/pom.xml @@ -0,0 +1,79 @@ + + + + + api-gateway + com.iluwatar + 1.13.0-SNAPSHOT + + + 4.0.0 + price-microservice + jar + + + + + org.springframework.boot + spring-boot-dependencies + + + + + + org.springframework + spring-webmvc + + + org.springframework.boot + spring-boot-starter-web + + + junit + junit + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + \ No newline at end of file diff --git a/api-gateway/price-microservice/src/main/java/com/iluwatar/price/microservice/PriceApplication.java b/api-gateway/price-microservice/src/main/java/com/iluwatar/price/microservice/PriceApplication.java new file mode 100644 index 000000000..e777c7c2b --- /dev/null +++ b/api-gateway/price-microservice/src/main/java/com/iluwatar/price/microservice/PriceApplication.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.price.microservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * PriceApplication starts up Spring Boot, exposing endpoints for the Price microservice through + * the {@link PriceController}. + */ +@SpringBootApplication +public class PriceApplication { + + /** + * Microservice entry point + * @param args + * command line args + */ + public static void main(String[] args) { + SpringApplication.run(PriceApplication.class, args); + } +} diff --git a/api-gateway/price-microservice/src/main/java/com/iluwatar/price/microservice/PriceController.java b/api-gateway/price-microservice/src/main/java/com/iluwatar/price/microservice/PriceController.java new file mode 100644 index 000000000..ec43618fa --- /dev/null +++ b/api-gateway/price-microservice/src/main/java/com/iluwatar/price/microservice/PriceController.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.price.microservice; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +/** + * Exposes the Price microservice's endpoints + */ +@RestController +public class PriceController { + + /** + * An endpoint for a user to retrieve a product's price + * @return A product's price + */ + @RequestMapping(value = "/price", method = RequestMethod.GET) + public String getPrice() { + return "20"; + } +} diff --git a/api-gateway/price-microservice/src/main/resources/application.properties b/api-gateway/price-microservice/src/main/resources/application.properties new file mode 100644 index 000000000..f0b479dfd --- /dev/null +++ b/api-gateway/price-microservice/src/main/resources/application.properties @@ -0,0 +1,24 @@ +# +# The MIT License +# Copyright (c) 2014 Ilkka Seppälä +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +server.port=50006 \ No newline at end of file diff --git a/api-gateway/price-microservice/src/test/java/com/iluwatar/price/microservice/PriceControllerTest.java b/api-gateway/price-microservice/src/test/java/com/iluwatar/price/microservice/PriceControllerTest.java new file mode 100644 index 000000000..c289549f2 --- /dev/null +++ b/api-gateway/price-microservice/src/test/java/com/iluwatar/price/microservice/PriceControllerTest.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.price.microservice; + +import org.junit.Assert; +import org.junit.Test; + +public class PriceControllerTest { + @Test + public void testgetPrice() { + PriceController priceController = new PriceController(); + + String price = priceController.getPrice(); + + Assert.assertEquals("20", price); + } +} diff --git a/async-method-invocation/README.md b/async-method-invocation/README.md new file mode 100644 index 000000000..2d99820c5 --- /dev/null +++ b/async-method-invocation/README.md @@ -0,0 +1,33 @@ +--- +layout: pattern +title: Async Method Invocation +folder: async-method-invocation +permalink: /patterns/async-method-invocation/ +categories: Concurrency +tags: + - Java + - Difficulty-Intermediate + - Functional + - Reactive +--- + +## Intent +Asynchronous method invocation is pattern where the calling thread +is not blocked while waiting results of tasks. The pattern provides parallel +processing of multiple independent tasks and retrieving the results via +callbacks or waiting until everything is done. + +![alt text](./etc/async-method-invocation.png "Async Method Invocation") + +## Applicability +Use async method invocation pattern when + +* you have multiple independent tasks that can run in parallel +* you need to improve the performance of a group of sequential tasks +* you have limited amount of processing capacity or long running tasks and the + caller should not wait the tasks to be ready + +## Real world examples + +* [FutureTask](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/FutureTask.html), [CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html) and [ExecutorService](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html) (Java) +* [Task-based Asynchronous Pattern](https://msdn.microsoft.com/en-us/library/hh873175.aspx) (.NET) diff --git a/async-method-invocation/etc/async-method-invocation.png b/async-method-invocation/etc/async-method-invocation.png new file mode 100644 index 000000000..764895d7a Binary files /dev/null and b/async-method-invocation/etc/async-method-invocation.png differ diff --git a/async-method-invocation/etc/async-method-invocation.ucls b/async-method-invocation/etc/async-method-invocation.ucls new file mode 100644 index 000000000..f2e189eae --- /dev/null +++ b/async-method-invocation/etc/async-method-invocation.ucls @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/async-method-invocation/pom.xml b/async-method-invocation/pom.xml new file mode 100644 index 000000000..d912df965 --- /dev/null +++ b/async-method-invocation/pom.xml @@ -0,0 +1,47 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + async-method-invocation + + + junit + junit + test + + + org.mockito + mockito-core + test + + + diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java new file mode 100644 index 000000000..0a56dc166 --- /dev/null +++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java @@ -0,0 +1,125 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.async.method.invocation; + +import java.util.concurrent.Callable; + +/** + * This application demonstrates the async method invocation pattern. Key parts of the pattern are + * AsyncResult which is an intermediate container for an asynchronously evaluated value, + * AsyncCallback which can be provided to be executed on task completion and AsyncExecutor + * that manages the execution of the async tasks. + *

+ * The main method shows example flow of async invocations. The main thread starts multiple tasks with variable + * durations and then continues its own work. When the main thread has done it's job it collects the results of the + * async tasks. Two of the tasks are handled with callbacks, meaning the callbacks are executed immediately when the + * tasks complete. + *

+ * Noteworthy difference of thread usage between the async results and callbacks is that the async results are collected + * in the main thread but the callbacks are executed within the worker threads. This should be noted when working with + * thread pools. + *

+ * Java provides its own implementations of async method invocation pattern. FutureTask, CompletableFuture and + * ExecutorService are the real world implementations of this pattern. But due to the nature of parallel programming, + * the implementations are not trivial. This example does not take all possible scenarios into account but rather + * provides a simple version that helps to understand the pattern. + * + * @see AsyncResult + * @see AsyncCallback + * @see AsyncExecutor + * + * @see java.util.concurrent.FutureTask + * @see java.util.concurrent.CompletableFuture + * @see java.util.concurrent.ExecutorService + */ +public class App { + + /** + * Program entry point + */ + public static void main(String[] args) throws Exception { + // construct a new executor that will run async tasks + AsyncExecutor executor = new ThreadAsyncExecutor(); + + // start few async tasks with varying processing times, two last with callback handlers + AsyncResult asyncResult1 = executor.startProcess(lazyval(10, 500)); + AsyncResult asyncResult2 = executor.startProcess(lazyval("test", 300)); + AsyncResult asyncResult3 = executor.startProcess(lazyval(50L, 700)); + AsyncResult asyncResult4 = executor.startProcess(lazyval(20, 400), callback("Callback result 4")); + AsyncResult asyncResult5 = executor.startProcess(lazyval("callback", 600), callback("Callback result 5")); + + // emulate processing in the current thread while async tasks are running in their own threads + Thread.sleep(350); // Oh boy I'm working hard here + log("Some hard work done"); + + // wait for completion of the tasks + Integer result1 = executor.endProcess(asyncResult1); + String result2 = executor.endProcess(asyncResult2); + Long result3 = executor.endProcess(asyncResult3); + asyncResult4.await(); + asyncResult5.await(); + + // log the results of the tasks, callbacks log immediately when complete + log("Result 1: " + result1); + log("Result 2: " + result2); + log("Result 3: " + result3); + } + + /** + * Creates a callable that lazily evaluates to given value with artificial delay. + * + * @param value + * value to evaluate + * @param delayMillis + * artificial delay in milliseconds + * @return new callable for lazy evaluation + */ + private static Callable lazyval(T value, long delayMillis) { + return () -> { + Thread.sleep(delayMillis); + log("Task completed with: " + value); + return value; + }; + } + + /** + * Creates a simple callback that logs the complete status of the async result. + * + * @param name + * callback name + * @return new async callback + */ + private static AsyncCallback callback(String name) { + return (value, ex) -> { + if (ex.isPresent()) { + log(name + " failed: " + ex.map(Exception::getMessage).orElse("")); + } else { + log(name + ": " + value); + } + }; + } + + private static void log(String msg) { + System.out.println(String.format("[%1$-10s] - %2$s", Thread.currentThread().getName(), msg)); + } +} diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncCallback.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncCallback.java new file mode 100644 index 000000000..2fddba683 --- /dev/null +++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncCallback.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.async.method.invocation; + +import java.util.Optional; + +/** + * + * AsyncCallback interface + * + * @param + * + */ +public interface AsyncCallback { + + /** + * Complete handler which is executed when async task is completed or fails execution. + * + * @param value the evaluated value from async task, undefined when execution fails + * @param ex empty value if execution succeeds, some exception if executions fails + */ + void onComplete(T value, Optional ex); +} diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java new file mode 100644 index 000000000..eb1afa4f3 --- /dev/null +++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java @@ -0,0 +1,63 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.async.method.invocation; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; + +/** + * + * AsyncExecutor interface + * + */ +public interface AsyncExecutor { + + /** + * Starts processing of an async task. Returns immediately with async result. + * + * @param task task to be executed asynchronously + * @return async result for the task + */ + AsyncResult startProcess(Callable task); + + /** + * Starts processing of an async task. Returns immediately with async result. Executes callback + * when the task is completed. + * + * @param task task to be executed asynchronously + * @param callback callback to be executed on task completion + * @return async result for the task + */ + AsyncResult startProcess(Callable task, AsyncCallback callback); + + /** + * Ends processing of an async task. Blocks the current thread if necessary and returns the + * evaluated value of the completed task. + * + * @param asyncResult async result of a task + * @return evaluated value of the completed task + * @throws ExecutionException if execution has failed, containing the root cause + * @throws InterruptedException if the execution is interrupted + */ + T endProcess(AsyncResult asyncResult) throws ExecutionException, InterruptedException; +} diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java new file mode 100644 index 000000000..bcd97adbc --- /dev/null +++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java @@ -0,0 +1,55 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.async.method.invocation; + +import java.util.concurrent.ExecutionException; + +/** + * + * AsyncResult interface + */ +public interface AsyncResult { + + /** + * Status of the async task execution. + * + * @return true if execution is completed or failed + */ + boolean isCompleted(); + + /** + * Gets the value of completed async task. + * + * @return evaluated value or throws ExecutionException if execution has failed + * @throws ExecutionException if execution has failed, containing the root cause + * @throws IllegalStateException if execution is not completed + */ + T getValue() throws ExecutionException; + + /** + * Blocks the current thread until the async task is completed. + * + * @throws InterruptedException if the execution is interrupted + */ + void await() throws InterruptedException; +} diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java new file mode 100644 index 000000000..7f96d9ab7 --- /dev/null +++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java @@ -0,0 +1,150 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.async.method.invocation; + +import java.util.Optional; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * + * Implementation of async executor that creates a new thread for every task. + * + */ +public class ThreadAsyncExecutor implements AsyncExecutor { + + /** Index for thread naming */ + private final AtomicInteger idx = new AtomicInteger(0); + + @Override + public AsyncResult startProcess(Callable task) { + return startProcess(task, null); + } + + @Override + public AsyncResult startProcess(Callable task, AsyncCallback callback) { + CompletableResult result = new CompletableResult<>(callback); + new Thread(() -> { + try { + result.setValue(task.call()); + } catch (Exception ex) { + result.setException(ex); + } + } , "executor-" + idx.incrementAndGet()).start(); + return result; + } + + @Override + public T endProcess(AsyncResult asyncResult) throws ExecutionException, InterruptedException { + if (asyncResult.isCompleted()) { + return asyncResult.getValue(); + } else { + asyncResult.await(); + return asyncResult.getValue(); + } + } + + /** + * Simple implementation of async result that allows completing it successfully with a value or exceptionally with an + * exception. A really simplified version from its real life cousins FutureTask and CompletableFuture. + * + * @see java.util.concurrent.FutureTask + * @see java.util.concurrent.CompletableFuture + */ + private static class CompletableResult implements AsyncResult { + + static final int RUNNING = 1; + static final int FAILED = 2; + static final int COMPLETED = 3; + + final Object lock; + final Optional> callback; + + volatile int state = RUNNING; + T value; + Exception exception; + + CompletableResult(AsyncCallback callback) { + this.lock = new Object(); + this.callback = Optional.ofNullable(callback); + } + + /** + * Sets the value from successful execution and executes callback if available. Notifies any thread waiting for + * completion. + * + * @param value + * value of the evaluated task + */ + void setValue(T value) { + this.value = value; + this.state = COMPLETED; + this.callback.ifPresent(ac -> ac.onComplete(value, Optional.empty())); + synchronized (lock) { + lock.notifyAll(); + } + } + + /** + * Sets the exception from failed execution and executes callback if available. Notifies any thread waiting for + * completion. + * + * @param exception + * exception of the failed task + */ + void setException(Exception exception) { + this.exception = exception; + this.state = FAILED; + this.callback.ifPresent(ac -> ac.onComplete(null, Optional.of(exception))); + synchronized (lock) { + lock.notifyAll(); + } + } + + @Override + public boolean isCompleted() { + return state > RUNNING; + } + + @Override + public T getValue() throws ExecutionException { + if (state == COMPLETED) { + return value; + } else if (state == FAILED) { + throw new ExecutionException(exception); + } else { + throw new IllegalStateException("Execution not completed yet"); + } + } + + @Override + public void await() throws InterruptedException { + synchronized (lock) { + if (!isCompleted()) { + lock.wait(); + } + } + } + } +} diff --git a/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java new file mode 100644 index 000000000..117a75f2a --- /dev/null +++ b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.async.method.invocation; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() throws Exception { + String[] args = {}; + App.main(args); + } +} diff --git a/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java new file mode 100644 index 000000000..b4a23222a --- /dev/null +++ b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java @@ -0,0 +1,312 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.async.method.invocation; + +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Matchers; + +import java.util.Optional; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +/** + * Date: 12/6/15 - 10:49 AM + * + * @author Jeroen Meulemeester + */ +public class ThreadAsyncExecutorTest { + + /** + * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} + */ + @Test(timeout = 3000) + public void testSuccessfulTaskWithoutCallback() throws Exception { + // Instantiate a new executor and start a new 'null' task ... + final ThreadAsyncExecutor executor = new ThreadAsyncExecutor(); + + final Object result = new Object(); + final Callable task = mock(Callable.class); + when(task.call()).thenReturn(result); + + final AsyncResult asyncResult = executor.startProcess(task); + assertNotNull(asyncResult); + asyncResult.await(); // Prevent timing issues, and wait until the result is available + assertTrue(asyncResult.isCompleted()); + + // Our task should only execute once ... + verify(task, times(1)).call(); + + // ... and the result should be exactly the same object + assertSame(result, asyncResult.getValue()); + } + + /** + * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} + */ + @Test(timeout = 3000) + public void testSuccessfulTaskWithCallback() throws Exception { + // Instantiate a new executor and start a new 'null' task ... + final ThreadAsyncExecutor executor = new ThreadAsyncExecutor(); + + final Object result = new Object(); + final Callable task = mock(Callable.class); + when(task.call()).thenReturn(result); + + final AsyncCallback callback = mock(AsyncCallback.class); + final AsyncResult asyncResult = executor.startProcess(task, callback); + assertNotNull(asyncResult); + asyncResult.await(); // Prevent timing issues, and wait until the result is available + assertTrue(asyncResult.isCompleted()); + + // Our task should only execute once ... + verify(task, times(1)).call(); + + // ... same for the callback, we expect our object + final ArgumentCaptor> optionalCaptor = ArgumentCaptor.forClass((Class) Optional.class); + verify(callback, times(1)).onComplete(eq(result), optionalCaptor.capture()); + + final Optional optionalException = optionalCaptor.getValue(); + assertNotNull(optionalException); + assertFalse(optionalException.isPresent()); + + // ... and the result should be exactly the same object + assertSame(result, asyncResult.getValue()); + } + + /** + * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} when a task takes a while + * to execute + */ + @Test(timeout = 5000) + public void testLongRunningTaskWithoutCallback() throws Exception { + // Instantiate a new executor and start a new 'null' task ... + final ThreadAsyncExecutor executor = new ThreadAsyncExecutor(); + + final Object result = new Object(); + final Callable task = mock(Callable.class); + when(task.call()).thenAnswer(i -> { + Thread.sleep(1500); + return result; + }); + + final AsyncResult asyncResult = executor.startProcess(task); + assertNotNull(asyncResult); + assertFalse(asyncResult.isCompleted()); + + try { + asyncResult.getValue(); + fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task"); + } catch (IllegalStateException e) { + assertNotNull(e.getMessage()); + } + + // Our task should only execute once, but it can take a while ... + verify(task, timeout(3000).times(1)).call(); + + // Prevent timing issues, and wait until the result is available + asyncResult.await(); + assertTrue(asyncResult.isCompleted()); + verifyNoMoreInteractions(task); + + // ... and the result should be exactly the same object + assertSame(result, asyncResult.getValue()); + } + + /** + * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when a task + * takes a while to execute + */ + @Test(timeout = 5000) + public void testLongRunningTaskWithCallback() throws Exception { + // Instantiate a new executor and start a new 'null' task ... + final ThreadAsyncExecutor executor = new ThreadAsyncExecutor(); + + final Object result = new Object(); + final Callable task = mock(Callable.class); + when(task.call()).thenAnswer(i -> { + Thread.sleep(1500); + return result; + }); + + final AsyncCallback callback = mock(AsyncCallback.class); + final AsyncResult asyncResult = executor.startProcess(task, callback); + assertNotNull(asyncResult); + assertFalse(asyncResult.isCompleted()); + + verifyZeroInteractions(callback); + + try { + asyncResult.getValue(); + fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task"); + } catch (IllegalStateException e) { + assertNotNull(e.getMessage()); + } + + // Our task should only execute once, but it can take a while ... + verify(task, timeout(3000).times(1)).call(); + + final ArgumentCaptor> optionalCaptor = ArgumentCaptor.forClass((Class) Optional.class); + verify(callback, timeout(3000).times(1)).onComplete(eq(result), optionalCaptor.capture()); + + final Optional optionalException = optionalCaptor.getValue(); + assertNotNull(optionalException); + assertFalse(optionalException.isPresent()); + + // Prevent timing issues, and wait until the result is available + asyncResult.await(); + assertTrue(asyncResult.isCompleted()); + verifyNoMoreInteractions(task, callback); + + // ... and the result should be exactly the same object + assertSame(result, asyncResult.getValue()); + } + + /** + * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} when a task takes a while + * to execute, while waiting on the result using {@link ThreadAsyncExecutor#endProcess(AsyncResult)} + */ + @Test(timeout = 5000) + public void testEndProcess() throws Exception { + // Instantiate a new executor and start a new 'null' task ... + final ThreadAsyncExecutor executor = new ThreadAsyncExecutor(); + + final Object result = new Object(); + final Callable task = mock(Callable.class); + when(task.call()).thenAnswer(i -> { + Thread.sleep(1500); + return result; + }); + + final AsyncResult asyncResult = executor.startProcess(task); + assertNotNull(asyncResult); + assertFalse(asyncResult.isCompleted()); + + try { + asyncResult.getValue(); + fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task"); + } catch (IllegalStateException e) { + assertNotNull(e.getMessage()); + } + + assertSame(result, executor.endProcess(asyncResult)); + verify(task, times(1)).call(); + assertTrue(asyncResult.isCompleted()); + + // Calling end process a second time while already finished should give the same result + assertSame(result, executor.endProcess(asyncResult)); + verifyNoMoreInteractions(task); + } + + /** + * Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable)} when the callable is 'null' + */ + @Test(timeout = 3000) + public void testNullTask() throws Exception { + // Instantiate a new executor and start a new 'null' task ... + final ThreadAsyncExecutor executor = new ThreadAsyncExecutor(); + final AsyncResult asyncResult = executor.startProcess(null); + + assertNotNull("The AsyncResult should not be 'null', even though the task was 'null'.", asyncResult); + asyncResult.await(); // Prevent timing issues, and wait until the result is available + assertTrue(asyncResult.isCompleted()); + + try { + asyncResult.getValue(); + fail("Expected ExecutionException with NPE as cause"); + } catch (final ExecutionException e) { + assertNotNull(e.getMessage()); + assertNotNull(e.getCause()); + assertEquals(NullPointerException.class, e.getCause().getClass()); + } + + } + + /** + * Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when the + * callable is 'null', but the asynchronous callback is provided + */ + @Test(timeout = 3000) + public void testNullTaskWithCallback() throws Exception { + // Instantiate a new executor and start a new 'null' task ... + final ThreadAsyncExecutor executor = new ThreadAsyncExecutor(); + final AsyncCallback callback = mock(AsyncCallback.class); + final AsyncResult asyncResult = executor.startProcess(null, callback); + + assertNotNull("The AsyncResult should not be 'null', even though the task was 'null'.", asyncResult); + asyncResult.await(); // Prevent timing issues, and wait until the result is available + assertTrue(asyncResult.isCompleted()); + + final ArgumentCaptor> optionalCaptor = ArgumentCaptor.forClass((Class) Optional.class); + verify(callback, times(1)).onComplete(Matchers.isNull(), optionalCaptor.capture()); + + final Optional optionalException = optionalCaptor.getValue(); + assertNotNull(optionalException); + assertTrue(optionalException.isPresent()); + + final Exception exception = optionalException.get(); + assertNotNull(exception); + assertEquals(NullPointerException.class, exception.getClass()); + + try { + asyncResult.getValue(); + fail("Expected ExecutionException with NPE as cause"); + } catch (final ExecutionException e) { + assertNotNull(e.getMessage()); + assertNotNull(e.getCause()); + assertEquals(NullPointerException.class, e.getCause().getClass()); + } + + } + + /** + * Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when both + * the callable and the asynchronous callback are 'null' + */ + @Test(timeout = 3000) + public void testNullTaskWithNullCallback() throws Exception { + // Instantiate a new executor and start a new 'null' task ... + final ThreadAsyncExecutor executor = new ThreadAsyncExecutor(); + final AsyncResult asyncResult = executor.startProcess(null, null); + + assertNotNull("The AsyncResult should not be 'null', even though the task and callback were 'null'.", asyncResult); + asyncResult.await(); // Prevent timing issues, and wait until the result is available + assertTrue(asyncResult.isCompleted()); + + try { + asyncResult.getValue(); + fail("Expected ExecutionException with NPE as cause"); + } catch (final ExecutionException e) { + assertNotNull(e.getMessage()); + assertNotNull(e.getCause()); + assertEquals(NullPointerException.class, e.getCause().getClass()); + } + + } + +} \ No newline at end of file diff --git a/bridge/README.md b/bridge/README.md new file mode 100644 index 000000000..49dad14e4 --- /dev/null +++ b/bridge/README.md @@ -0,0 +1,33 @@ +--- +layout: pattern +title: Bridge +folder: bridge +permalink: /patterns/bridge/ +categories: Structural +tags: + - Java + - Gang Of Four + - Difficulty-Intermediate +--- + +## Also known as +Handle/Body + +## Intent +Decouple an abstraction from its implementation so that the two can +vary independently. + +![alt text](./etc/bridge.png "Bridge") + +## Applicability +Use the Bridge pattern when + +* you want to avoid a permanent binding between an abstraction and its implementation. This might be the case, for example, when the implementation must be selected or switched at run-time. +* both the abstractions and their implementations should be extensible by subclassing. In this case, the Bridge pattern lets you combine the different abstractions and implementations and extend them independently +* changes in the implementation of an abstraction should have no impact on clients; that is, their code should not have to be recompiled. +* you have a proliferation of classes. Such a class hierarchy indicates the need for splitting an object into two parts. Rumbaugh uses the term "nested generalizations" to refer to such class hierarchies +* you want to share an implementation among multiple objects (perhaps using reference counting), and this fact should be hidden from the client. A simple example is Coplien's String class, in which multiple objects can share the same string representation. + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/bridge/etc/bridge.png b/bridge/etc/bridge.png index 472fe51e7..00d3e611c 100644 Binary files a/bridge/etc/bridge.png and b/bridge/etc/bridge.png differ diff --git a/bridge/etc/bridge.ucls b/bridge/etc/bridge.ucls index 86455e7fb..2eda6e1d1 100644 --- a/bridge/etc/bridge.ucls +++ b/bridge/etc/bridge.ucls @@ -1,18 +1,18 @@ - - + + - - + + @@ -21,25 +21,25 @@ - + - - + + - - + + @@ -48,7 +48,7 @@ - + @@ -57,7 +57,7 @@ - + @@ -66,7 +66,7 @@ - + @@ -75,7 +75,7 @@ - + @@ -84,7 +84,7 @@ - + @@ -93,7 +93,7 @@ - + @@ -101,48 +101,52 @@ - - + + - - - - - - - - - - - - - - - + + + - + - - + + + + + + - + + + + + + + + + + + + + - - + + diff --git a/bridge/etc/bridge_1.png b/bridge/etc/bridge_1.png deleted file mode 100644 index 3152efdf6..000000000 Binary files a/bridge/etc/bridge_1.png and /dev/null differ diff --git a/bridge/pom.xml b/bridge/pom.xml index 4718a01bf..a7a3883c8 100644 --- a/bridge/pom.xml +++ b/bridge/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT bridge @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/bridge/src/main/java/com/iluwatar/bridge/App.java b/bridge/src/main/java/com/iluwatar/bridge/App.java index 3797653d4..27b37b6b3 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/App.java +++ b/bridge/src/main/java/com/iluwatar/bridge/App.java @@ -1,35 +1,61 @@ -package com.iluwatar.bridge; - -/** - * - * In Bridge pattern both abstraction (MagicWeapon) and implementation - * (MagicWeaponImp) have their own class hierarchies. The interface of the - * implementations can be changed without affecting the clients. - * - */ -public class App { - - public static void main(String[] args) { - BlindingMagicWeapon blindingMagicWeapon = new BlindingMagicWeapon( - new Excalibur()); - blindingMagicWeapon.wield(); - blindingMagicWeapon.blind(); - blindingMagicWeapon.swing(); - blindingMagicWeapon.unwield(); - - FlyingMagicWeapon flyingMagicWeapon = new FlyingMagicWeapon( - new Mjollnir()); - flyingMagicWeapon.wield(); - flyingMagicWeapon.fly(); - flyingMagicWeapon.swing(); - flyingMagicWeapon.unwield(); - - SoulEatingMagicWeapon soulEatingMagicWeapon = new SoulEatingMagicWeapon( - new Stormbringer()); - soulEatingMagicWeapon.wield(); - soulEatingMagicWeapon.swing(); - soulEatingMagicWeapon.eatSoul(); - soulEatingMagicWeapon.unwield(); - - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bridge; + +/** + * + * The Bridge pattern can also be thought of as two layers of abstraction. With Bridge, you can + * decouple an abstraction from its implementation so that the two can vary independently. + *

+ * In Bridge pattern both abstraction ({@link MagicWeapon}) and implementation ( + * {@link MagicWeaponImpl}) have their own class hierarchies. The interface of the implementations + * can be changed without affecting the clients. + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + BlindingMagicWeapon blindingMagicWeapon = new BlindingMagicWeapon(new Excalibur()); + blindingMagicWeapon.wield(); + blindingMagicWeapon.blind(); + blindingMagicWeapon.swing(); + blindingMagicWeapon.unwield(); + + FlyingMagicWeapon flyingMagicWeapon = new FlyingMagicWeapon(new Mjollnir()); + flyingMagicWeapon.wield(); + flyingMagicWeapon.fly(); + flyingMagicWeapon.swing(); + flyingMagicWeapon.unwield(); + + SoulEatingMagicWeapon soulEatingMagicWeapon = new SoulEatingMagicWeapon(new Stormbringer()); + soulEatingMagicWeapon.wield(); + soulEatingMagicWeapon.swing(); + soulEatingMagicWeapon.eatSoul(); + soulEatingMagicWeapon.unwield(); + } +} diff --git a/bridge/src/main/java/com/iluwatar/bridge/BlindingMagicWeapon.java b/bridge/src/main/java/com/iluwatar/bridge/BlindingMagicWeapon.java index 5421c6d5e..045039ef0 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/BlindingMagicWeapon.java +++ b/bridge/src/main/java/com/iluwatar/bridge/BlindingMagicWeapon.java @@ -1,33 +1,59 @@ -package com.iluwatar.bridge; - -public class BlindingMagicWeapon extends MagicWeapon { - - public BlindingMagicWeapon(BlindingMagicWeaponImp imp) { - super(imp); - } - - @Override - public BlindingMagicWeaponImp getImp() { - return (BlindingMagicWeaponImp) imp; - } - - @Override - public void wield() { - getImp().wieldImp(); - } - - @Override - public void swing() { - getImp().swingImp(); - } - - @Override - public void unwield() { - getImp().unwieldImp(); - } - - public void blind() { - getImp().blindImp(); - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bridge; + +/** + * + * BlindingMagicWeapon + * + */ +public class BlindingMagicWeapon extends MagicWeapon { + + public BlindingMagicWeapon(BlindingMagicWeaponImpl imp) { + super(imp); + } + + @Override + public BlindingMagicWeaponImpl getImp() { + return (BlindingMagicWeaponImpl) imp; + } + + @Override + public void wield() { + getImp().wieldImp(); + } + + @Override + public void swing() { + getImp().swingImp(); + } + + @Override + public void unwield() { + getImp().unwieldImp(); + } + + public void blind() { + getImp().blindImp(); + } +} diff --git a/bridge/src/main/java/com/iluwatar/bridge/BlindingMagicWeaponImp.java b/bridge/src/main/java/com/iluwatar/bridge/BlindingMagicWeaponImp.java deleted file mode 100644 index 0686ce917..000000000 --- a/bridge/src/main/java/com/iluwatar/bridge/BlindingMagicWeaponImp.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.iluwatar.bridge; - -public abstract class BlindingMagicWeaponImp extends MagicWeaponImp { - - public abstract void blindImp(); - -} diff --git a/bridge/src/main/java/com/iluwatar/bridge/BlindingMagicWeaponImpl.java b/bridge/src/main/java/com/iluwatar/bridge/BlindingMagicWeaponImpl.java new file mode 100644 index 000000000..83f31e709 --- /dev/null +++ b/bridge/src/main/java/com/iluwatar/bridge/BlindingMagicWeaponImpl.java @@ -0,0 +1,34 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bridge; + +/** + * + * BlindingMagicWeaponImpl + * + */ +public abstract class BlindingMagicWeaponImpl extends MagicWeaponImpl { + + public abstract void blindImp(); + +} diff --git a/bridge/src/main/java/com/iluwatar/bridge/Excalibur.java b/bridge/src/main/java/com/iluwatar/bridge/Excalibur.java index 52298c900..2523b3557 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/Excalibur.java +++ b/bridge/src/main/java/com/iluwatar/bridge/Excalibur.java @@ -1,26 +1,51 @@ -package com.iluwatar.bridge; - -public class Excalibur extends BlindingMagicWeaponImp { - - @Override - public void wieldImp() { - System.out.println("wielding Excalibur"); - } - - @Override - public void swingImp() { - System.out.println("swinging Excalibur"); - } - - @Override - public void unwieldImp() { - System.out.println("unwielding Excalibur"); - } - - @Override - public void blindImp() { - System.out - .println("bright light streams from Excalibur blinding the enemy"); - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bridge; + +/** + * + * Excalibur + * + */ +public class Excalibur extends BlindingMagicWeaponImpl { + + @Override + public void wieldImp() { + System.out.println("wielding Excalibur"); + } + + @Override + public void swingImp() { + System.out.println("swinging Excalibur"); + } + + @Override + public void unwieldImp() { + System.out.println("unwielding Excalibur"); + } + + @Override + public void blindImp() { + System.out.println("bright light streams from Excalibur blinding the enemy"); + } +} diff --git a/bridge/src/main/java/com/iluwatar/bridge/FlyingMagicWeapon.java b/bridge/src/main/java/com/iluwatar/bridge/FlyingMagicWeapon.java index 9cb1902f9..0988c179c 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/FlyingMagicWeapon.java +++ b/bridge/src/main/java/com/iluwatar/bridge/FlyingMagicWeapon.java @@ -1,32 +1,59 @@ -package com.iluwatar.bridge; - -public class FlyingMagicWeapon extends MagicWeapon { - - public FlyingMagicWeapon(FlyingMagicWeaponImp imp) { - super(imp); - } - - public FlyingMagicWeaponImp getImp() { - return (FlyingMagicWeaponImp) imp; - } - - @Override - public void wield() { - getImp().wieldImp(); - } - - @Override - public void swing() { - getImp().swingImp(); - } - - @Override - public void unwield() { - getImp().unwieldImp(); - } - - public void fly() { - getImp().flyImp(); - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bridge; + +/** + * + * FlyingMagicWeapon + * + */ +public class FlyingMagicWeapon extends MagicWeapon { + + public FlyingMagicWeapon(FlyingMagicWeaponImpl imp) { + super(imp); + } + + public FlyingMagicWeaponImpl getImp() { + return (FlyingMagicWeaponImpl) imp; + } + + @Override + public void wield() { + getImp().wieldImp(); + } + + @Override + public void swing() { + getImp().swingImp(); + } + + @Override + public void unwield() { + getImp().unwieldImp(); + } + + public void fly() { + getImp().flyImp(); + } + +} diff --git a/bridge/src/main/java/com/iluwatar/bridge/FlyingMagicWeaponImp.java b/bridge/src/main/java/com/iluwatar/bridge/FlyingMagicWeaponImp.java deleted file mode 100644 index 37fa8a0f9..000000000 --- a/bridge/src/main/java/com/iluwatar/bridge/FlyingMagicWeaponImp.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.iluwatar.bridge; - -public abstract class FlyingMagicWeaponImp extends MagicWeaponImp { - - public abstract void flyImp(); - -} diff --git a/bridge/src/main/java/com/iluwatar/bridge/FlyingMagicWeaponImpl.java b/bridge/src/main/java/com/iluwatar/bridge/FlyingMagicWeaponImpl.java new file mode 100644 index 000000000..4bdb8801b --- /dev/null +++ b/bridge/src/main/java/com/iluwatar/bridge/FlyingMagicWeaponImpl.java @@ -0,0 +1,34 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bridge; + +/** + * + * FlyingMagicWeaponImpl + * + */ +public abstract class FlyingMagicWeaponImpl extends MagicWeaponImpl { + + public abstract void flyImp(); + +} diff --git a/bridge/src/main/java/com/iluwatar/bridge/MagicWeapon.java b/bridge/src/main/java/com/iluwatar/bridge/MagicWeapon.java index b03435c51..038da7c4f 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/MagicWeapon.java +++ b/bridge/src/main/java/com/iluwatar/bridge/MagicWeapon.java @@ -1,26 +1,47 @@ -package com.iluwatar.bridge; - -/** - * - * Abstraction interface. - * - */ -public abstract class MagicWeapon { - - protected MagicWeaponImp imp; - - public MagicWeapon(MagicWeaponImp imp) { - this.imp = imp; - } - - public abstract void wield(); - - public abstract void swing(); - - public abstract void unwield(); - - public MagicWeaponImp getImp() { - return imp; - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bridge; + +/** + * + * MagicWeapon + * + */ +public abstract class MagicWeapon { + + protected MagicWeaponImpl imp; + + public MagicWeapon(MagicWeaponImpl imp) { + this.imp = imp; + } + + public abstract void wield(); + + public abstract void swing(); + + public abstract void unwield(); + + public MagicWeaponImpl getImp() { + return imp; + } +} diff --git a/bridge/src/main/java/com/iluwatar/bridge/MagicWeaponImp.java b/bridge/src/main/java/com/iluwatar/bridge/MagicWeaponImp.java deleted file mode 100644 index cf972c115..000000000 --- a/bridge/src/main/java/com/iluwatar/bridge/MagicWeaponImp.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.iluwatar.bridge; - -/** - * - * Implementation interface. - * - */ -public abstract class MagicWeaponImp { - - public abstract void wieldImp(); - - public abstract void swingImp(); - - public abstract void unwieldImp(); - -} diff --git a/bridge/src/main/java/com/iluwatar/bridge/MagicWeaponImpl.java b/bridge/src/main/java/com/iluwatar/bridge/MagicWeaponImpl.java new file mode 100644 index 000000000..01f91825b --- /dev/null +++ b/bridge/src/main/java/com/iluwatar/bridge/MagicWeaponImpl.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bridge; + +/** + * + * MagicWeaponImpl + * + */ +public abstract class MagicWeaponImpl { + + public abstract void wieldImp(); + + public abstract void swingImp(); + + public abstract void unwieldImp(); + +} diff --git a/bridge/src/main/java/com/iluwatar/bridge/Mjollnir.java b/bridge/src/main/java/com/iluwatar/bridge/Mjollnir.java index 0c21f8d07..0cc31b471 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/Mjollnir.java +++ b/bridge/src/main/java/com/iluwatar/bridge/Mjollnir.java @@ -1,26 +1,51 @@ -package com.iluwatar.bridge; - -public class Mjollnir extends FlyingMagicWeaponImp { - - @Override - public void wieldImp() { - System.out.println("wielding Mjollnir"); - } - - @Override - public void swingImp() { - System.out.println("swinging Mjollnir"); - } - - @Override - public void unwieldImp() { - System.out.println("unwielding Mjollnir"); - } - - @Override - public void flyImp() { - System.out - .println("Mjollnir hits the enemy in the air and returns back to the owner's hand"); - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bridge; + +/** + * + * Mjollnir + * + */ +public class Mjollnir extends FlyingMagicWeaponImpl { + + @Override + public void wieldImp() { + System.out.println("wielding Mjollnir"); + } + + @Override + public void swingImp() { + System.out.println("swinging Mjollnir"); + } + + @Override + public void unwieldImp() { + System.out.println("unwielding Mjollnir"); + } + + @Override + public void flyImp() { + System.out.println("Mjollnir hits the enemy in the air and returns back to the owner's hand"); + } +} diff --git a/bridge/src/main/java/com/iluwatar/bridge/SoulEatingMagicWeapon.java b/bridge/src/main/java/com/iluwatar/bridge/SoulEatingMagicWeapon.java index dbbec102d..d62f7644b 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/SoulEatingMagicWeapon.java +++ b/bridge/src/main/java/com/iluwatar/bridge/SoulEatingMagicWeapon.java @@ -1,33 +1,60 @@ -package com.iluwatar.bridge; - -public class SoulEatingMagicWeapon extends MagicWeapon { - - public SoulEatingMagicWeapon(SoulEatingMagicWeaponImp imp) { - super(imp); - } - - @Override - public SoulEatingMagicWeaponImp getImp() { - return (SoulEatingMagicWeaponImp) imp; - } - - @Override - public void wield() { - getImp().wieldImp(); - } - - @Override - public void swing() { - getImp().swingImp(); - } - - @Override - public void unwield() { - getImp().unwieldImp(); - } - - public void eatSoul() { - getImp().eatSoulImp(); - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bridge; + +/** + * + * SoulEatingMagicWeapon + * + */ +public class SoulEatingMagicWeapon extends MagicWeapon { + + public SoulEatingMagicWeapon(SoulEatingMagicWeaponImpl imp) { + super(imp); + } + + @Override + public SoulEatingMagicWeaponImpl getImp() { + return (SoulEatingMagicWeaponImpl) imp; + } + + @Override + public void wield() { + getImp().wieldImp(); + } + + @Override + public void swing() { + getImp().swingImp(); + } + + @Override + public void unwield() { + getImp().unwieldImp(); + } + + public void eatSoul() { + getImp().eatSoulImp(); + } + +} diff --git a/bridge/src/main/java/com/iluwatar/bridge/SoulEatingMagicWeaponImp.java b/bridge/src/main/java/com/iluwatar/bridge/SoulEatingMagicWeaponImp.java deleted file mode 100644 index 3c1a557dd..000000000 --- a/bridge/src/main/java/com/iluwatar/bridge/SoulEatingMagicWeaponImp.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.iluwatar.bridge; - -public abstract class SoulEatingMagicWeaponImp extends MagicWeaponImp { - - public abstract void eatSoulImp(); - -} diff --git a/bridge/src/main/java/com/iluwatar/bridge/SoulEatingMagicWeaponImpl.java b/bridge/src/main/java/com/iluwatar/bridge/SoulEatingMagicWeaponImpl.java new file mode 100644 index 000000000..ad8bec48f --- /dev/null +++ b/bridge/src/main/java/com/iluwatar/bridge/SoulEatingMagicWeaponImpl.java @@ -0,0 +1,34 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bridge; + +/** + * + * SoulEatingMagicWeaponImpl + * + */ +public abstract class SoulEatingMagicWeaponImpl extends MagicWeaponImpl { + + public abstract void eatSoulImp(); + +} diff --git a/bridge/src/main/java/com/iluwatar/bridge/Stormbringer.java b/bridge/src/main/java/com/iluwatar/bridge/Stormbringer.java index 55b5960c9..91cad9cc2 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/Stormbringer.java +++ b/bridge/src/main/java/com/iluwatar/bridge/Stormbringer.java @@ -1,25 +1,51 @@ -package com.iluwatar.bridge; - -public class Stormbringer extends SoulEatingMagicWeaponImp { - - @Override - public void wieldImp() { - System.out.println("wielding Stormbringer"); - } - - @Override - public void swingImp() { - System.out.println("swinging Stormbringer"); - } - - @Override - public void unwieldImp() { - System.out.println("unwielding Stormbringer"); - } - - @Override - public void eatSoulImp() { - System.out.println("Stormbringer devours the enemy's soul"); - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bridge; + +/** + * + * Stormbringer + * + */ +public class Stormbringer extends SoulEatingMagicWeaponImpl { + + @Override + public void wieldImp() { + System.out.println("wielding Stormbringer"); + } + + @Override + public void swingImp() { + System.out.println("swinging Stormbringer"); + } + + @Override + public void unwieldImp() { + System.out.println("unwielding Stormbringer"); + } + + @Override + public void eatSoulImp() { + System.out.println("Stormbringer devours the enemy's soul"); + } +} diff --git a/bridge/src/test/java/com/iluwatar/bridge/AppTest.java b/bridge/src/test/java/com/iluwatar/bridge/AppTest.java index 196008950..1b9b63d7f 100644 --- a/bridge/src/test/java/com/iluwatar/bridge/AppTest.java +++ b/bridge/src/test/java/com/iluwatar/bridge/AppTest.java @@ -1,14 +1,39 @@ -package com.iluwatar.bridge; - -import org.junit.Test; - -import com.iluwatar.bridge.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bridge; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/bridge/src/test/java/com/iluwatar/bridge/BlindingMagicWeaponTest.java b/bridge/src/test/java/com/iluwatar/bridge/BlindingMagicWeaponTest.java new file mode 100644 index 000000000..54435555e --- /dev/null +++ b/bridge/src/test/java/com/iluwatar/bridge/BlindingMagicWeaponTest.java @@ -0,0 +1,55 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bridge; + +import org.junit.Test; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +/** + * Date: 12/6/15 - 11:15 PM + * + * @author Jeroen Meulemeester + */ +public class BlindingMagicWeaponTest extends MagicWeaponTest { + + /** + * Invoke all possible actions on the weapon and check if the actions are executed on the actual + * underlying weapon implementation. + */ + @Test + public void testExcalibur() throws Exception { + final Excalibur excalibur = spy(new Excalibur()); + final BlindingMagicWeapon blindingMagicWeapon = new BlindingMagicWeapon(excalibur); + + testBasicWeaponActions(blindingMagicWeapon, excalibur); + + blindingMagicWeapon.blind(); + verify(excalibur, times(1)).blindImp(); + verifyNoMoreInteractions(excalibur); + } + +} diff --git a/bridge/src/test/java/com/iluwatar/bridge/FlyingMagicWeaponTest.java b/bridge/src/test/java/com/iluwatar/bridge/FlyingMagicWeaponTest.java new file mode 100644 index 000000000..9053a7bb0 --- /dev/null +++ b/bridge/src/test/java/com/iluwatar/bridge/FlyingMagicWeaponTest.java @@ -0,0 +1,55 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bridge; + +import org.junit.Test; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +/** + * Date: 12/6/15 - 11:26 PM + * + * @author Jeroen Meulemeester + */ +public class FlyingMagicWeaponTest extends MagicWeaponTest { + + /** + * Invoke all possible actions on the weapon and check if the actions are executed on the actual + * underlying weapon implementation. + */ + @Test + public void testMjollnir() throws Exception { + final Mjollnir mjollnir = spy(new Mjollnir()); + final FlyingMagicWeapon flyingMagicWeapon = new FlyingMagicWeapon(mjollnir); + + testBasicWeaponActions(flyingMagicWeapon, mjollnir); + + flyingMagicWeapon.fly(); + verify(mjollnir, times(1)).flyImp(); + verifyNoMoreInteractions(mjollnir); + } + +} \ No newline at end of file diff --git a/bridge/src/test/java/com/iluwatar/bridge/MagicWeaponTest.java b/bridge/src/test/java/com/iluwatar/bridge/MagicWeaponTest.java new file mode 100644 index 000000000..6408fd256 --- /dev/null +++ b/bridge/src/test/java/com/iluwatar/bridge/MagicWeaponTest.java @@ -0,0 +1,64 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bridge; + +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +/** + * Date: 12/6/15 - 11:28 PM + * + * @author Jeroen Meulemeester + */ +public abstract class MagicWeaponTest { + + /** + * Invoke the basic actions of the given weapon, and test if the underlying weapon implementation + * is invoked + * + * @param weaponImpl The spied weapon implementation where actions are bridged to + * @param weapon The weapon, handled by the app + */ + protected final void testBasicWeaponActions(final MagicWeapon weapon, + final MagicWeaponImpl weaponImpl) { + assertNotNull(weapon); + assertNotNull(weaponImpl); + assertNotNull(weapon.getImp()); + + weapon.swing(); + verify(weaponImpl, times(1)).swingImp(); + verifyNoMoreInteractions(weaponImpl); + + weapon.wield(); + verify(weaponImpl, times(1)).wieldImp(); + verifyNoMoreInteractions(weaponImpl); + + weapon.unwield(); + verify(weaponImpl, times(1)).unwieldImp(); + verifyNoMoreInteractions(weaponImpl); + + } + +} diff --git a/bridge/src/test/java/com/iluwatar/bridge/SoulEatingMagicWeaponTest.java b/bridge/src/test/java/com/iluwatar/bridge/SoulEatingMagicWeaponTest.java new file mode 100644 index 000000000..0bd0a6b8d --- /dev/null +++ b/bridge/src/test/java/com/iluwatar/bridge/SoulEatingMagicWeaponTest.java @@ -0,0 +1,55 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bridge; + +import org.junit.Test; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +/** + * Date: 12/6/15 - 11:43 PM + * + * @author Jeroen Meulemeester + */ +public class SoulEatingMagicWeaponTest extends MagicWeaponTest { + + /** + * Invoke all possible actions on the weapon and check if the actions are executed on the actual + * underlying weapon implementation. + */ + @Test + public void testStormBringer() throws Exception { + final Stormbringer stormbringer = spy(new Stormbringer()); + final SoulEatingMagicWeapon soulEatingMagicWeapon = new SoulEatingMagicWeapon(stormbringer); + + testBasicWeaponActions(soulEatingMagicWeapon, stormbringer); + + soulEatingMagicWeapon.eatSoul(); + verify(stormbringer, times(1)).eatSoulImp(); + verifyNoMoreInteractions(stormbringer); + } + +} \ No newline at end of file diff --git a/builder/README.md b/builder/README.md new file mode 100644 index 000000000..5d1f3d24d --- /dev/null +++ b/builder/README.md @@ -0,0 +1,34 @@ +--- +layout: pattern +title: Builder +folder: builder +permalink: /patterns/builder/ +categories: Creational +tags: + - Java + - Gang Of Four + - Difficulty-Intermediate +--- + +## Intent +Separate the construction of a complex object from its +representation so that the same construction process can create different +representations. + +![alt text](./etc/builder_1.png "Builder") + +## Applicability +Use the Builder pattern when + +* the algorithm for creating a complex object should be independent of the parts that make up the object and how they're assembled +* the construction process must allow different representations for the object that's constructed + +## Real world examples + +* [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html) +* [Apache Camel builders](https://github.com/apache/camel/tree/0e195428ee04531be27a0b659005e3aa8d159d23/camel-core/src/main/java/org/apache/camel/builder) + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) +* [Effective Java (2nd Edition)](http://www.amazon.com/Effective-Java-Edition-Joshua-Bloch/dp/0321356683) diff --git a/builder/etc/builder.ucls b/builder/etc/builder.ucls index 06a83ced7..073c5cea7 100644 --- a/builder/etc/builder.ucls +++ b/builder/etc/builder.ucls @@ -28,7 +28,7 @@ - + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT builder diff --git a/builder/src/main/java/com/iluwatar/builder/App.java b/builder/src/main/java/com/iluwatar/builder/App.java index e38e5a0c3..d421de7b6 100644 --- a/builder/src/main/java/com/iluwatar/builder/App.java +++ b/builder/src/main/java/com/iluwatar/builder/App.java @@ -1,38 +1,77 @@ -package com.iluwatar.builder; - -import com.iluwatar. builder.Hero.HeroBuilder; - -/** - * - * This is the Builder pattern variation as described by Joshua Bloch in - * Effective Java 2nd Edition. - * - * We want to build Hero objects, but its construction is complex because of the - * many parameters needed. To aid the user we introduce HeroBuilder class. - * HeroBuilder takes the minimum parameters to build Hero object in its - * constructor. After that additional configuration for the Hero object can be - * done using the fluent HeroBuilder interface. When configuration is ready the - * build method is called to receive the final Hero object. - * - */ -public class App { - - public static void main(String[] args) { - - Hero mage = new HeroBuilder(Profession.MAGE, "Riobard") - .withHairColor(HairColor.BLACK).withWeapon(Weapon.DAGGER) - .build(); - System.out.println(mage); - - Hero warrior = new HeroBuilder(Profession.WARRIOR, "Amberjill") - .withHairColor(HairColor.BLOND) - .withHairType(HairType.LONG_CURLY).withArmor(Armor.CHAIN_MAIL) - .withWeapon(Weapon.SWORD).build(); - System.out.println(warrior); - - Hero thief = new HeroBuilder(Profession.THIEF, "Desmond") - .withHairType(HairType.BALD).withWeapon(Weapon.BOW).build(); - System.out.println(thief); - - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.builder; + +import com.iluwatar.builder.Hero.Builder; + +/** + * + * The intention of the Builder pattern is to find a solution to the telescoping constructor + * anti-pattern. The telescoping constructor anti-pattern occurs when the increase of object + * constructor parameter combination leads to an exponential list of constructors. Instead of using + * numerous constructors, the builder pattern uses another object, a builder, that receives each + * initialization parameter step by step and then returns the resulting constructed object at once. + *

+ * The Builder pattern has another benefit. It can be used for objects that contain flat data (html + * code, SQL query, X.509 certificate...), that is to say, data that can't be easily edited. This + * type of data cannot be edited step by step and must be edited at once. The best way to construct + * such an object is to use a builder class. + *

+ * In this example we have the Builder pattern variation as described by Joshua Bloch in Effective + * Java 2nd Edition. + *

+ * We want to build {@link Hero} objects, but its construction is complex because of the many + * parameters needed. To aid the user we introduce {@link Builder} class. {@link Hero.Builder} + * takes the minimum parameters to build {@link Hero} object in its constructor. After that + * additional configuration for the {@link Hero} object can be done using the fluent + * {@link Builder} interface. When configuration is ready the build method is called to receive + * the final {@link Hero} object. + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + + Hero mage = + new Hero.Builder(Profession.MAGE, "Riobard").withHairColor(HairColor.BLACK) + .withWeapon(Weapon.DAGGER).build(); + System.out.println(mage); + + Hero warrior = + new Hero.Builder(Profession.WARRIOR, "Amberjill").withHairColor(HairColor.BLOND) + .withHairType(HairType.LONG_CURLY).withArmor(Armor.CHAIN_MAIL).withWeapon(Weapon.SWORD) + .build(); + System.out.println(warrior); + + Hero thief = + new Hero.Builder(Profession.THIEF, "Desmond").withHairType(HairType.BALD) + .withWeapon(Weapon.BOW).build(); + System.out.println(thief); + + } +} diff --git a/builder/src/main/java/com/iluwatar/builder/Armor.java b/builder/src/main/java/com/iluwatar/builder/Armor.java index ebec6a47d..a0a4fe582 100644 --- a/builder/src/main/java/com/iluwatar/builder/Armor.java +++ b/builder/src/main/java/com/iluwatar/builder/Armor.java @@ -1,17 +1,44 @@ -package com.iluwatar.builder; - -public enum Armor { - - CLOTHES("clothes"), LEATHER("leather"), CHAIN_MAIL("chain mail"), PLATE_MAIL("plate mail"); - - private String title; - - Armor(String title) { - this.title = title; - } - - @Override - public String toString() { - return title; - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.builder; + +/** + * + * Armor enumeration + * + */ +public enum Armor { + + CLOTHES("clothes"), LEATHER("leather"), CHAIN_MAIL("chain mail"), PLATE_MAIL("plate mail"); + + private String title; + + Armor(String title) { + this.title = title; + } + + @Override + public String toString() { + return title; + } +} diff --git a/builder/src/main/java/com/iluwatar/builder/HairColor.java b/builder/src/main/java/com/iluwatar/builder/HairColor.java index a73044754..3c3195cc6 100644 --- a/builder/src/main/java/com/iluwatar/builder/HairColor.java +++ b/builder/src/main/java/com/iluwatar/builder/HairColor.java @@ -1,12 +1,39 @@ -package com.iluwatar.builder; - -public enum HairColor { - - WHITE, BLOND, RED, BROWN, BLACK; - - @Override - public String toString() { - return name().toLowerCase(); - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.builder; + +/** + * + * HairColor enumeration + * + */ +public enum HairColor { + + WHITE, BLOND, RED, BROWN, BLACK; + + @Override + public String toString() { + return name().toLowerCase(); + } + +} diff --git a/builder/src/main/java/com/iluwatar/builder/HairType.java b/builder/src/main/java/com/iluwatar/builder/HairType.java index 075699a58..8d15b0f30 100644 --- a/builder/src/main/java/com/iluwatar/builder/HairType.java +++ b/builder/src/main/java/com/iluwatar/builder/HairType.java @@ -1,17 +1,45 @@ -package com.iluwatar.builder; - -public enum HairType { - - BALD("bald"), SHORT("short"), CURLY("curly"), LONG_STRAIGHT("long straight"), LONG_CURLY("long curly"); - - private String title; - - HairType(String title) { - this.title = title; - } - - @Override - public String toString() { - return title; - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.builder; + +/** + * + * HairType enumeration + * + */ +public enum HairType { + + BALD("bald"), SHORT("short"), CURLY("curly"), LONG_STRAIGHT("long straight"), LONG_CURLY( + "long curly"); + + private String title; + + HairType(String title) { + this.title = title; + } + + @Override + public String toString() { + return title; + } +} diff --git a/builder/src/main/java/com/iluwatar/builder/Hero.java b/builder/src/main/java/com/iluwatar/builder/Hero.java index 75513eeda..25445851a 100644 --- a/builder/src/main/java/com/iluwatar/builder/Hero.java +++ b/builder/src/main/java/com/iluwatar/builder/Hero.java @@ -1,129 +1,149 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.builder; /** * - * The class with many parameters. + * Hero, the class with many parameters. * */ -public class Hero { +public final class Hero { - private final Profession profession; - private final String name; - private final HairType hairType; - private final HairColor hairColor; - private final Armor armor; - private final Weapon weapon; + private final Profession profession; + private final String name; + private final HairType hairType; + private final HairColor hairColor; + private final Armor armor; + private final Weapon weapon; - public Profession getProfession() { - return profession; - } + private Hero(Builder builder) { + this.profession = builder.profession; + this.name = builder.name; + this.hairColor = builder.hairColor; + this.hairType = builder.hairType; + this.weapon = builder.weapon; + this.armor = builder.armor; + } - public String getName() { - return name; - } + public Profession getProfession() { + return profession; + } - public HairType getHairType() { - return hairType; - } + public String getName() { + return name; + } - public HairColor getHairColor() { - return hairColor; - } + public HairType getHairType() { + return hairType; + } - public Armor getArmor() { - return armor; - } + public HairColor getHairColor() { + return hairColor; + } - public Weapon getWeapon() { - return weapon; - } + public Armor getArmor() { + return armor; + } - @Override - public String toString() { + public Weapon getWeapon() { + return weapon; + } - StringBuilder sb = new StringBuilder(); - sb.append("This is a "); - sb.append(profession); - sb.append(" named "); - sb.append(name); - if (hairColor != null || hairType != null) { - sb.append(" with "); - if (hairColor != null) { - sb.append(hairColor); - sb.append(" "); - } - if (hairType != null) { - sb.append(hairType); - sb.append(" "); - } - sb.append(hairType != HairType.BALD ? "hair" : "head"); - } - if (armor != null) { - sb.append(" wearing "); - sb.append(armor); - } - if (weapon != null) { - sb.append(" and wielding a "); - sb.append(weapon); - } - sb.append("."); - return sb.toString(); - } + @Override + public String toString() { - private Hero(HeroBuilder builder) { - this.profession = builder.profession; - this.name = builder.name; - this.hairColor = builder.hairColor; - this.hairType = builder.hairType; - this.weapon = builder.weapon; - this.armor = builder.armor; - } + StringBuilder sb = new StringBuilder(); + sb.append("This is a ") + .append(profession) + .append(" named ") + .append(name); + if (hairColor != null || hairType != null) { + sb.append(" with "); + if (hairColor != null) { + sb.append(hairColor).append(' '); + } + if (hairType != null) { + sb.append(hairType).append(' '); + } + sb.append(hairType != HairType.BALD ? "hair" : "head"); + } + if (armor != null) { + sb.append(" wearing ").append(armor); + } + if (weapon != null) { + sb.append(" and wielding a ").append(weapon); + } + sb.append('.'); + return sb.toString(); + } - /** - * - * The builder class. - * - */ - public static class HeroBuilder { + /** + * + * The builder class. + * + */ + public static class Builder { - private final Profession profession; - private final String name; - private HairType hairType; - private HairColor hairColor; - private Armor armor; - private Weapon weapon; + private final Profession profession; + private final String name; + private HairType hairType; + private HairColor hairColor; + private Armor armor; + private Weapon weapon; - public HeroBuilder(Profession profession, String name) { - if (profession == null || name == null) { - throw new IllegalArgumentException( - "profession and name can not be null"); - } - this.profession = profession; - this.name = name; - } + /** + * Constructor + */ + public Builder(Profession profession, String name) { + if (profession == null || name == null) { + throw new IllegalArgumentException("profession and name can not be null"); + } + this.profession = profession; + this.name = name; + } - public HeroBuilder withHairType(HairType hairType) { - this.hairType = hairType; - return this; - } + public Builder withHairType(HairType hairType) { + this.hairType = hairType; + return this; + } - public HeroBuilder withHairColor(HairColor hairColor) { - this.hairColor = hairColor; - return this; - } + public Builder withHairColor(HairColor hairColor) { + this.hairColor = hairColor; + return this; + } - public HeroBuilder withArmor(Armor armor) { - this.armor = armor; - return this; - } + public Builder withArmor(Armor armor) { + this.armor = armor; + return this; + } - public HeroBuilder withWeapon(Weapon weapon) { - this.weapon = weapon; - return this; - } + public Builder withWeapon(Weapon weapon) { + this.weapon = weapon; + return this; + } - public Hero build() { - return new Hero(this); - } - } + public Hero build() { + return new Hero(this); + } + } } diff --git a/builder/src/main/java/com/iluwatar/builder/Profession.java b/builder/src/main/java/com/iluwatar/builder/Profession.java index 487fc5133..7be5b999e 100644 --- a/builder/src/main/java/com/iluwatar/builder/Profession.java +++ b/builder/src/main/java/com/iluwatar/builder/Profession.java @@ -1,12 +1,38 @@ -package com.iluwatar.builder; - -public enum Profession { - - WARRIOR, THIEF, MAGE, PRIEST; - - @Override - public String toString() { - return name().toLowerCase(); - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.builder; + +/** + * + * Profession enumeration + * + */ +public enum Profession { + + WARRIOR, THIEF, MAGE, PRIEST; + + @Override + public String toString() { + return name().toLowerCase(); + } +} diff --git a/builder/src/main/java/com/iluwatar/builder/Weapon.java b/builder/src/main/java/com/iluwatar/builder/Weapon.java index 99622ddac..97d347d24 100644 --- a/builder/src/main/java/com/iluwatar/builder/Weapon.java +++ b/builder/src/main/java/com/iluwatar/builder/Weapon.java @@ -1,12 +1,38 @@ -package com.iluwatar.builder; - -public enum Weapon { - - DAGGER, SWORD, AXE, WARHAMMER, BOW; - - @Override - public String toString() { - return name().toLowerCase(); - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.builder; + +/** + * + * Weapon enumeration + * + */ +public enum Weapon { + + DAGGER, SWORD, AXE, WARHAMMER, BOW; + + @Override + public String toString() { + return name().toLowerCase(); + } +} diff --git a/builder/src/test/java/com/iluwatar/builder/AppTest.java b/builder/src/test/java/com/iluwatar/builder/AppTest.java index 462ad3a9a..e83db9f9f 100644 --- a/builder/src/test/java/com/iluwatar/builder/AppTest.java +++ b/builder/src/test/java/com/iluwatar/builder/AppTest.java @@ -1,14 +1,39 @@ -package com.iluwatar.builder; - -import org.junit.Test; - -import com.iluwatar. builder.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.builder; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/builder/src/test/java/com/iluwatar/builder/HeroTest.java b/builder/src/test/java/com/iluwatar/builder/HeroTest.java new file mode 100644 index 000000000..b0a73a9d0 --- /dev/null +++ b/builder/src/test/java/com/iluwatar/builder/HeroTest.java @@ -0,0 +1,78 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.builder; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/6/15 - 11:01 PM + * + * @author Jeroen Meulemeester + */ +public class HeroTest { + + /** + * Test if we get the expected exception when trying to create a hero without a profession + */ + @Test(expected = IllegalArgumentException.class) + public void testMissingProfession() throws Exception { + new Hero.Builder(null, "Sir without a job"); + } + + /** + * Test if we get the expected exception when trying to create a hero without a name + */ + @Test(expected = IllegalArgumentException.class) + public void testMissingName() throws Exception { + new Hero.Builder(Profession.THIEF, null); + } + + /** + * Test if the hero build by the builder has the correct attributes, as requested + */ + @Test + public void testBuildHero() throws Exception { + final String heroName = "Sir Lancelot"; + + final Hero hero = new Hero.Builder(Profession.WARRIOR, heroName) + .withArmor(Armor.CHAIN_MAIL) + .withWeapon(Weapon.SWORD) + .withHairType(HairType.LONG_CURLY) + .withHairColor(HairColor.BLOND) + .build(); + + assertNotNull(hero); + assertNotNull(hero.toString()); + assertEquals(Profession.WARRIOR, hero.getProfession()); + assertEquals(heroName, hero.getName()); + assertEquals(Armor.CHAIN_MAIL, hero.getArmor()); + assertEquals(Weapon.SWORD, hero.getWeapon()); + assertEquals(HairType.LONG_CURLY, hero.getHairType()); + assertEquals(HairColor.BLOND, hero.getHairColor()); + + } + +} \ No newline at end of file diff --git a/business-delegate/README.md b/business-delegate/README.md new file mode 100644 index 000000000..e6e249122 --- /dev/null +++ b/business-delegate/README.md @@ -0,0 +1,29 @@ +--- +layout: pattern +title: Business Delegate +folder: business-delegate +permalink: /patterns/business-delegate/ +categories: Business Tier +tags: + - Java + - Difficulty-Intermediate +--- + +## Intent +The Business Delegate pattern adds an abstraction layer between +presentation and business tiers. By using the pattern we gain loose coupling +between the tiers and encapsulate knowledge about how to locate, connect to, +and interact with the business objects that make up the application. + +![alt text](./etc/business-delegate.png "Business Delegate") + +## Applicability +Use the Business Delegate pattern when + +* you want loose coupling between presentation and business tiers +* you want to orchestrate calls to multiple business services +* you want to encapsulate service lookups and service calls + +## Credits + +* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) diff --git a/business-delegate/etc/business-delegate.png b/business-delegate/etc/business-delegate.png new file mode 100644 index 000000000..928cf9346 Binary files /dev/null and b/business-delegate/etc/business-delegate.png differ diff --git a/business-delegate/etc/business-delegate.ucls b/business-delegate/etc/business-delegate.ucls new file mode 100644 index 000000000..668a6579e --- /dev/null +++ b/business-delegate/etc/business-delegate.ucls @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/business-delegate/pom.xml b/business-delegate/pom.xml new file mode 100644 index 000000000..c6f7e0c37 --- /dev/null +++ b/business-delegate/pom.xml @@ -0,0 +1,48 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + business-delegate + + + junit + junit + test + + + org.mockito + mockito-core + test + + + diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/App.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/App.java new file mode 100644 index 000000000..e9a264322 --- /dev/null +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/App.java @@ -0,0 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.business.delegate; + +/** + * The Business Delegate pattern adds an abstraction layer between the presentation and business + * tiers. By using the pattern we gain loose coupling between the tiers. The Business Delegate + * encapsulates knowledge about how to locate, connect to, and interact with the business objects + * that make up the application. + * + *

Some of the services the Business Delegate uses are instantiated directly, and some can be + * retrieved through service lookups. The Business Delegate itself may contain business logic too + * potentially tying together multiple service calls, exception handling, retrying etc. + * + *

In this example the client ({@link Client}) utilizes a business delegate ( + * {@link BusinessDelegate}) to execute a task. The Business Delegate then selects the appropriate + * service and makes the service call. + */ +public class App { + + /** + * Program entry point. + * + * @param args command line args + */ + public static void main(String[] args) { + + BusinessDelegate businessDelegate = new BusinessDelegate(); + BusinessLookup businessLookup = new BusinessLookup(); + businessLookup.setEjbService(new EjbService()); + businessLookup.setJmsService(new JmsService()); + + businessDelegate.setLookupService(businessLookup); + businessDelegate.setServiceType(ServiceType.EJB); + + Client client = new Client(businessDelegate); + client.doTask(); + + businessDelegate.setServiceType(ServiceType.JMS); + client.doTask(); + } +} diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessDelegate.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessDelegate.java new file mode 100644 index 000000000..81aa9a0f1 --- /dev/null +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessDelegate.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.business.delegate; + +/** + * BusinessDelegate separates the presentation and business tiers + */ +public class BusinessDelegate { + + private BusinessLookup lookupService; + private BusinessService businessService; + private ServiceType serviceType; + + public void setLookupService(BusinessLookup businessLookup) { + this.lookupService = businessLookup; + } + + public void setServiceType(ServiceType serviceType) { + this.serviceType = serviceType; + } + + public void doTask() { + businessService = lookupService.getBusinessService(serviceType); + businessService.doProcessing(); + } +} diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessLookup.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessLookup.java new file mode 100644 index 000000000..7e1045e7f --- /dev/null +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessLookup.java @@ -0,0 +1,53 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.business.delegate; + +/** + * Class for performing service lookups. + */ +public class BusinessLookup { + + private EjbService ejbService; + + private JmsService jmsService; + + /** + * @param serviceType Type of service instance to be returned. + * @return Service instance. + */ + public BusinessService getBusinessService(ServiceType serviceType) { + if (serviceType.equals(ServiceType.EJB)) { + return ejbService; + } else { + return jmsService; + } + } + + public void setJmsService(JmsService jmsService) { + this.jmsService = jmsService; + } + + public void setEjbService(EjbService ejbService) { + this.ejbService = ejbService; + } +} diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessService.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessService.java new file mode 100644 index 000000000..f5784964a --- /dev/null +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessService.java @@ -0,0 +1,33 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.business.delegate; + +/** + * + * Interface for service implementations + * + */ +public interface BusinessService { + + void doProcessing(); +} diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/Client.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/Client.java new file mode 100644 index 000000000..8c6bf59a1 --- /dev/null +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/Client.java @@ -0,0 +1,41 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.business.delegate; + +/** + * + * Client utilizes BusinessDelegate to call the business tier + * + */ +public class Client { + + private BusinessDelegate businessDelegate; + + public Client(BusinessDelegate businessDelegate) { + this.businessDelegate = businessDelegate; + } + + public void doTask() { + businessDelegate.doTask(); + } +} diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/EjbService.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/EjbService.java new file mode 100644 index 000000000..7296f63b4 --- /dev/null +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/EjbService.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.business.delegate; + +/** + * + * Service EJB implementation + * + */ +public class EjbService implements BusinessService { + + @Override + public void doProcessing() { + System.out.println("EjbService is now processing"); + } +} diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/JmsService.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/JmsService.java new file mode 100644 index 000000000..5b71ce57d --- /dev/null +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/JmsService.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.business.delegate; + +/** + * + * Service JMS implementation + * + */ +public class JmsService implements BusinessService { + + @Override + public void doProcessing() { + System.out.println("JmsService is now processing"); + } +} diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/ServiceType.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/ServiceType.java new file mode 100644 index 000000000..06d0a42b9 --- /dev/null +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/ServiceType.java @@ -0,0 +1,33 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.business.delegate; + +/** + * + * Enumeration for service types + * + */ +public enum ServiceType { + + EJB, JMS; +} diff --git a/business-delegate/src/test/java/com/iluwatar/business/delegate/AppTest.java b/business-delegate/src/test/java/com/iluwatar/business/delegate/AppTest.java new file mode 100644 index 000000000..1767b7ac5 --- /dev/null +++ b/business-delegate/src/test/java/com/iluwatar/business/delegate/AppTest.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.business.delegate; + +import org.junit.Test; + +import java.io.IOException; + +/** + * Tests that Business Delegate example runs without errors. + */ +public class AppTest { + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } +} diff --git a/business-delegate/src/test/java/com/iluwatar/business/delegate/BusinessDelegateTest.java b/business-delegate/src/test/java/com/iluwatar/business/delegate/BusinessDelegateTest.java new file mode 100644 index 000000000..aec4ea14b --- /dev/null +++ b/business-delegate/src/test/java/com/iluwatar/business/delegate/BusinessDelegateTest.java @@ -0,0 +1,100 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.business.delegate; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import org.junit.Before; +import org.junit.Test; + +/** + * The Business Delegate pattern adds an abstraction layer between the presentation and business + * tiers. By using the pattern we gain loose coupling between the tiers. The Business Delegate + * encapsulates knowledge about how to locate, connect to, and interact with the business objects + * that make up the application. + * + *

Some of the services the Business Delegate uses are instantiated directly, and some can be + * retrieved through service lookups. The Business Delegate itself may contain business logic too + * potentially tying together multiple service calls, exception handling, retrying etc. + */ +public class BusinessDelegateTest { + + private EjbService ejbService; + + private JmsService jmsService; + + private BusinessLookup businessLookup; + + private BusinessDelegate businessDelegate; + + /** + * This method sets up the instance variables of this test class. It is executed before the + * execution of every test. + */ + @Before + public void setup() { + ejbService = spy(new EjbService()); + jmsService = spy(new JmsService()); + + businessLookup = spy(new BusinessLookup()); + businessLookup.setEjbService(ejbService); + businessLookup.setJmsService(jmsService); + + businessDelegate = spy(new BusinessDelegate()); + businessDelegate.setLookupService(businessLookup); + } + + /** + * In this example the client ({@link Client}) utilizes a business delegate ( + * {@link BusinessDelegate}) to execute a task. The Business Delegate then selects the appropriate + * service and makes the service call. + */ + @Test + public void testBusinessDelegate() { + + // setup a client object + Client client = new Client(businessDelegate); + + // set the service type + businessDelegate.setServiceType(ServiceType.EJB); + + // action + client.doTask(); + + // verifying that the businessDelegate was used by client during doTask() method. + verify(businessDelegate).doTask(); + verify(ejbService).doProcessing(); + + // set the service type + businessDelegate.setServiceType(ServiceType.JMS); + + // action + client.doTask(); + + // verifying that the businessDelegate was used by client during doTask() method. + verify(businessDelegate, times(2)).doTask(); + verify(jmsService).doProcessing(); + } +} diff --git a/caching/.gitignore b/caching/.gitignore new file mode 100644 index 000000000..b83d22266 --- /dev/null +++ b/caching/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/caching/README.md b/caching/README.md new file mode 100644 index 000000000..2b89d0559 --- /dev/null +++ b/caching/README.md @@ -0,0 +1,28 @@ +--- +layout: pattern +title: Caching +folder: caching +permalink: /patterns/caching/ +categories: Other +tags: + - Java + - Difficulty-Intermediate + - Performance +--- + +## Intent +To avoid expensive re-acquisition of resources by not releasing +the resources immediately after their use. The resources retain their identity, are kept in some +fast-access storage, and are re-used to avoid having to acquire them again. + +![alt text](./etc/caching.png "Caching") + +## Applicability +Use the Caching pattern(s) when + +* Repetitious acquisition, initialization, and release of the same resource causes unnecessary performance overhead. + +## Credits + +* [Write-through, write-around, write-back: Cache explained](http://www.computerweekly.com/feature/Write-through-write-around-write-back-Cache-explained) +* [Read-Through, Write-Through, Write-Behind, and Refresh-Ahead Caching](https://docs.oracle.com/cd/E15357_01/coh.360/e15723/cache_rtwtwbra.htm#COHDG5177) diff --git a/caching/etc/caching.png b/caching/etc/caching.png new file mode 100644 index 000000000..6b3b2d055 Binary files /dev/null and b/caching/etc/caching.png differ diff --git a/caching/etc/caching.ucls b/caching/etc/caching.ucls new file mode 100644 index 000000000..815a62ec9 --- /dev/null +++ b/caching/etc/caching.ucls @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/caching/pom.xml b/caching/pom.xml new file mode 100644 index 000000000..c20842a89 --- /dev/null +++ b/caching/pom.xml @@ -0,0 +1,75 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + caching + + + junit + junit + test + + + org.mongodb + mongodb-driver + 3.0.4 + + + org.mongodb + mongodb-driver-core + 3.0.4 + + + org.mongodb + bson + 3.0.4 + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.19 + + false + + + + + diff --git a/caching/src/main/java/com/iluwatar/caching/App.java b/caching/src/main/java/com/iluwatar/caching/App.java new file mode 100644 index 000000000..8e5a84085 --- /dev/null +++ b/caching/src/main/java/com/iluwatar/caching/App.java @@ -0,0 +1,139 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.caching; + +/** + * + * The Caching pattern describes how to avoid expensive re-acquisition of resources by not releasing + * the resources immediately after their use. The resources retain their identity, are kept in some + * fast-access storage, and are re-used to avoid having to acquire them again. There are three main + * caching strategies/techniques in this pattern; each with their own pros and cons. They are: + * write-through which writes data to the cache and DB in a single transaction, + * write-around which writes data immediately into the DB instead of the cache, and + * write-behind which writes data into the cache initially whilst the data is only + * written into the DB when the cache is full. The read-through strategy is also + * included in the mentioned three strategies -- returns data from the cache to the caller if + * it exists else queries from DB and stores it into the cache for future use. These + * strategies determine when the data in the cache should be written back to the backing store (i.e. + * Database) and help keep both data sources synchronized/up-to-date. This pattern can improve + * performance and also helps to maintain consistency between data held in the cache and the data in + * the underlying data store. + *

+ * In this example, the user account ({@link UserAccount}) entity is used as the underlying + * application data. The cache itself is implemented as an internal (Java) data structure. It adopts + * a Least-Recently-Used (LRU) strategy for evicting data from itself when its full. The three + * strategies are individually tested. The testing of the cache is restricted towards saving and + * querying of user accounts from the underlying data store ( {@link DbManager}). The main class ( + * {@link App} is not aware of the underlying mechanics of the application (i.e. save and query) and + * whether the data is coming from the cache or the DB (i.e. separation of concern). The AppManager + * ({@link AppManager}) handles the transaction of data to-and-from the underlying data store + * (depending on the preferred caching policy/strategy). + * + * App --> AppManager --> CacheStore/LRUCache/CachingPolicy --> DBManager + *

+ * + * @see CacheStore + * @See LRUCache + * @see CachingPolicy + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + AppManager.initDb(false); // VirtualDB (instead of MongoDB) was used in running the JUnit tests + // and the App class to avoid Maven compilation errors. Set flag to + // true to run the tests with MongoDB (provided that MongoDB is + // installed and socket connection is open). + AppManager.initCacheCapacity(3); + App app = new App(); + app.useReadAndWriteThroughStrategy(); + app.useReadThroughAndWriteAroundStrategy(); + app.useReadThroughAndWriteBehindStrategy(); + } + + /** + * Read-through and write-through + */ + public void useReadAndWriteThroughStrategy() { + System.out.println("# CachingPolicy.THROUGH"); + AppManager.initCachingPolicy(CachingPolicy.THROUGH); + + UserAccount userAccount1 = new UserAccount("001", "John", "He is a boy."); + + AppManager.save(userAccount1); + System.out.println(AppManager.printCacheContent()); + AppManager.find("001"); + AppManager.find("001"); + } + + /** + * Read-through and write-around + */ + public void useReadThroughAndWriteAroundStrategy() { + System.out.println("# CachingPolicy.AROUND"); + AppManager.initCachingPolicy(CachingPolicy.AROUND); + + UserAccount userAccount2 = new UserAccount("002", "Jane", "She is a girl."); + + AppManager.save(userAccount2); + System.out.println(AppManager.printCacheContent()); + AppManager.find("002"); + System.out.println(AppManager.printCacheContent()); + userAccount2 = AppManager.find("002"); + userAccount2.setUserName("Jane G."); + AppManager.save(userAccount2); + System.out.println(AppManager.printCacheContent()); + AppManager.find("002"); + System.out.println(AppManager.printCacheContent()); + AppManager.find("002"); + } + + /** + * Read-through and write-behind + */ + public void useReadThroughAndWriteBehindStrategy() { + System.out.println("# CachingPolicy.BEHIND"); + AppManager.initCachingPolicy(CachingPolicy.BEHIND); + + UserAccount userAccount3 = new UserAccount("003", "Adam", "He likes food."); + UserAccount userAccount4 = new UserAccount("004", "Rita", "She hates cats."); + UserAccount userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard."); + + AppManager.save(userAccount3); + AppManager.save(userAccount4); + AppManager.save(userAccount5); + System.out.println(AppManager.printCacheContent()); + AppManager.find("003"); + System.out.println(AppManager.printCacheContent()); + UserAccount userAccount6 = new UserAccount("006", "Yasha", "She is an only child."); + AppManager.save(userAccount6); + System.out.println(AppManager.printCacheContent()); + AppManager.find("004"); + System.out.println(AppManager.printCacheContent()); + } +} diff --git a/caching/src/main/java/com/iluwatar/caching/AppManager.java b/caching/src/main/java/com/iluwatar/caching/AppManager.java new file mode 100644 index 000000000..2967c759f --- /dev/null +++ b/caching/src/main/java/com/iluwatar/caching/AppManager.java @@ -0,0 +1,109 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.caching; + +import java.text.ParseException; + +/** + * + * AppManager helps to bridge the gap in communication between the main class and the application's + * back-end. DB connection is initialized through this class. The chosen caching strategy/policy is + * also initialized here. Before the cache can be used, the size of the cache has to be set. + * Depending on the chosen caching policy, AppManager will call the appropriate function in the + * CacheStore class. + * + */ +public final class AppManager { + + private static CachingPolicy cachingPolicy; + + private AppManager() { + } + + /** + * + * Developer/Tester is able to choose whether the application should use MongoDB as its underlying + * data storage or a simple Java data structure to (temporarily) store the data/objects during + * runtime. + */ + public static void initDb(boolean useMongoDb) { + if (useMongoDb) { + try { + DbManager.connect(); + } catch (ParseException e) { + e.printStackTrace(); + } + } else { + DbManager.createVirtualDb(); + } + } + + /** + * Initialize caching policy + */ + public static void initCachingPolicy(CachingPolicy policy) { + cachingPolicy = policy; + if (cachingPolicy == CachingPolicy.BEHIND) { + Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { + @Override + public void run() { + CacheStore.flushCache(); + } + })); + } + CacheStore.clearCache(); + } + + public static void initCacheCapacity(int capacity) { + CacheStore.initCapacity(capacity); + } + + /** + * Find user account + */ + public static UserAccount find(String userId) { + if (cachingPolicy == CachingPolicy.THROUGH || cachingPolicy == CachingPolicy.AROUND) { + return CacheStore.readThrough(userId); + } else if (cachingPolicy == CachingPolicy.BEHIND) { + return CacheStore.readThroughWithWriteBackPolicy(userId); + } + return null; + } + + /** + * Save user account + */ + public static void save(UserAccount userAccount) { + if (cachingPolicy == CachingPolicy.THROUGH) { + CacheStore.writeThrough(userAccount); + } else if (cachingPolicy == CachingPolicy.AROUND) { + CacheStore.writeAround(userAccount); + } else if (cachingPolicy == CachingPolicy.BEHIND) { + CacheStore.writeBehind(userAccount); + } + } + + public static String printCacheContent() { + return CacheStore.print(); + } +} diff --git a/caching/src/main/java/com/iluwatar/caching/CacheStore.java b/caching/src/main/java/com/iluwatar/caching/CacheStore.java new file mode 100644 index 000000000..5903f8219 --- /dev/null +++ b/caching/src/main/java/com/iluwatar/caching/CacheStore.java @@ -0,0 +1,156 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.caching; + +import java.util.List; + +/** + * + * The caching strategies are implemented in this class. + * + */ +public class CacheStore { + + static LruCache cache; + + private CacheStore() { + } + + /** + * Init cache capacity + */ + public static void initCapacity(int capacity) { + if (null == cache) { + cache = new LruCache(capacity); + } else { + cache.setCapacity(capacity); + } + } + + /** + * Get user account using read-through cache + */ + public static UserAccount readThrough(String userId) { + if (cache.contains(userId)) { + System.out.println("# Cache Hit!"); + return cache.get(userId); + } + System.out.println("# Cache Miss!"); + UserAccount userAccount = DbManager.readFromDb(userId); + cache.set(userId, userAccount); + return userAccount; + } + + /** + * Get user account using write-through cache + */ + public static void writeThrough(UserAccount userAccount) { + if (cache.contains(userAccount.getUserId())) { + DbManager.updateDb(userAccount); + } else { + DbManager.writeToDb(userAccount); + } + cache.set(userAccount.getUserId(), userAccount); + } + + /** + * Get user account using write-around cache + */ + public static void writeAround(UserAccount userAccount) { + if (cache.contains(userAccount.getUserId())) { + DbManager.updateDb(userAccount); + cache.invalidate(userAccount.getUserId()); // Cache data has been updated -- remove older + // version from cache. + } else { + DbManager.writeToDb(userAccount); + } + } + + /** + * Get user account using read-through cache with write-back policy + */ + public static UserAccount readThroughWithWriteBackPolicy(String userId) { + if (cache.contains(userId)) { + System.out.println("# Cache Hit!"); + return cache.get(userId); + } + System.out.println("# Cache Miss!"); + UserAccount userAccount = DbManager.readFromDb(userId); + if (cache.isFull()) { + System.out.println("# Cache is FULL! Writing LRU data to DB..."); + UserAccount toBeWrittenToDb = cache.getLruData(); + DbManager.upsertDb(toBeWrittenToDb); + } + cache.set(userId, userAccount); + return userAccount; + } + + /** + * Set user account + */ + public static void writeBehind(UserAccount userAccount) { + if (cache.isFull() && !cache.contains(userAccount.getUserId())) { + System.out.println("# Cache is FULL! Writing LRU data to DB..."); + UserAccount toBeWrittenToDb = cache.getLruData(); + DbManager.upsertDb(toBeWrittenToDb); + } + cache.set(userAccount.getUserId(), userAccount); + } + + /** + * Clears cache + */ + public static void clearCache() { + if (null != cache) { + cache.clear(); + } + } + + /** + * Writes remaining content in the cache into the DB. + */ + public static void flushCache() { + System.out.println("# flushCache..."); + if (null == cache) { + return; + } + List listOfUserAccounts = cache.getCacheDataInListForm(); + for (UserAccount userAccount : listOfUserAccounts) { + DbManager.upsertDb(userAccount); + } + } + + /** + * Print user accounts + */ + public static String print() { + List listOfUserAccounts = cache.getCacheDataInListForm(); + StringBuilder sb = new StringBuilder(); + sb.append("\n--CACHE CONTENT--\n"); + for (UserAccount userAccount : listOfUserAccounts) { + sb.append(userAccount.toString() + "\n"); + } + sb.append("----\n"); + return sb.toString(); + } +} diff --git a/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java b/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java new file mode 100644 index 000000000..490113baa --- /dev/null +++ b/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java @@ -0,0 +1,42 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.caching; + +/** + * + * Enum class containing the three caching strategies implemented in the pattern. + * + */ +public enum CachingPolicy { + THROUGH("through"), AROUND("around"), BEHIND("behind"); + + private String policy; + + private CachingPolicy(String policy) { + this.policy = policy; + } + + public String getPolicy() { + return policy; + } +} diff --git a/caching/src/main/java/com/iluwatar/caching/DbManager.java b/caching/src/main/java/com/iluwatar/caching/DbManager.java new file mode 100644 index 000000000..c12461d0c --- /dev/null +++ b/caching/src/main/java/com/iluwatar/caching/DbManager.java @@ -0,0 +1,164 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.caching; + +import java.text.ParseException; +import java.util.HashMap; +import java.util.Map; + +import org.bson.Document; + +import com.mongodb.MongoClient; +import com.mongodb.client.FindIterable; +import com.mongodb.client.MongoDatabase; +import com.mongodb.client.model.UpdateOptions; + +/** + * + *

DBManager handles the communication with the underlying data store i.e. Database. It contains the + * implemented methods for querying, inserting, and updating data. MongoDB was used as the database + * for the application.

+ * + *

Developer/Tester is able to choose whether the application should use MongoDB as its underlying + * data storage (connect()) or a simple Java data structure to (temporarily) store the data/objects + * during runtime (createVirtualDB()).

+ * + */ +public final class DbManager { + + private static MongoClient mongoClient; + private static MongoDatabase db; + private static boolean useMongoDB; + + private static Map virtualDB; + + private DbManager() { + } + + /** + * Create DB + */ + public static void createVirtualDb() { + useMongoDB = false; + virtualDB = new HashMap<>(); + } + + /** + * Connect to DB + */ + public static void connect() throws ParseException { + useMongoDB = true; + mongoClient = new MongoClient(); + db = mongoClient.getDatabase("test"); + } + + /** + * Read user account from DB + */ + public static UserAccount readFromDb(String userId) { + if (!useMongoDB) { + if (virtualDB.containsKey(userId)) { + return virtualDB.get(userId); + } + return null; + } + if (null == db) { + try { + connect(); + } catch (ParseException e) { + e.printStackTrace(); + } + } + FindIterable iterable = + db.getCollection("user_accounts").find(new Document("userID", userId)); + if (iterable == null) { + return null; + } + Document doc = iterable.first(); + return new UserAccount(userId, doc.getString("userName"), doc.getString("additionalInfo")); + } + + /** + * Write user account to DB + */ + public static void writeToDb(UserAccount userAccount) { + if (!useMongoDB) { + virtualDB.put(userAccount.getUserId(), userAccount); + return; + } + if (null == db) { + try { + connect(); + } catch (ParseException e) { + e.printStackTrace(); + } + } + db.getCollection("user_accounts").insertOne( + new Document("userID", userAccount.getUserId()).append("userName", + userAccount.getUserName()).append("additionalInfo", userAccount.getAdditionalInfo())); + } + + /** + * Update DB + */ + public static void updateDb(UserAccount userAccount) { + if (!useMongoDB) { + virtualDB.put(userAccount.getUserId(), userAccount); + return; + } + if (null == db) { + try { + connect(); + } catch (ParseException e) { + e.printStackTrace(); + } + } + db.getCollection("user_accounts").updateOne( + new Document("userID", userAccount.getUserId()), + new Document("$set", new Document("userName", userAccount.getUserName()).append( + "additionalInfo", userAccount.getAdditionalInfo()))); + } + + /** + * + * Insert data into DB if it does not exist. Else, update it. + */ + public static void upsertDb(UserAccount userAccount) { + if (!useMongoDB) { + virtualDB.put(userAccount.getUserId(), userAccount); + return; + } + if (null == db) { + try { + connect(); + } catch (ParseException e) { + e.printStackTrace(); + } + } + db.getCollection("user_accounts").updateOne( + new Document("userID", userAccount.getUserId()), + new Document("$set", new Document("userID", userAccount.getUserId()).append("userName", + userAccount.getUserName()).append("additionalInfo", userAccount.getAdditionalInfo())), + new UpdateOptions().upsert(true)); + } +} diff --git a/caching/src/main/java/com/iluwatar/caching/LruCache.java b/caching/src/main/java/com/iluwatar/caching/LruCache.java new file mode 100644 index 000000000..5c5549afd --- /dev/null +++ b/caching/src/main/java/com/iluwatar/caching/LruCache.java @@ -0,0 +1,187 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.caching; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * + * Data structure/implementation of the application's cache. The data structure consists of a hash + * table attached with a doubly linked-list. The linked-list helps in capturing and maintaining the + * LRU data in the cache. When a data is queried (from the cache), added (to the cache), or updated, + * the data is moved to the front of the list to depict itself as the most-recently-used data. The + * LRU data is always at the end of the list. + * + */ +public class LruCache { + + class Node { + String userId; + UserAccount userAccount; + Node previous; + Node next; + + public Node(String userId, UserAccount userAccount) { + this.userId = userId; + this.userAccount = userAccount; + } + } + + int capacity; + Map cache = new HashMap<>(); + Node head; + Node end; + + public LruCache(int capacity) { + this.capacity = capacity; + } + + /** + * Get user account + */ + public UserAccount get(String userId) { + if (cache.containsKey(userId)) { + Node node = cache.get(userId); + remove(node); + setHead(node); + return node.userAccount; + } + return null; + } + + /** + * + * Remove node from linked list. + */ + public void remove(Node node) { + if (node.previous != null) { + node.previous.next = node.next; + } else { + head = node.next; + } + if (node.next != null) { + node.next.previous = node.previous; + } else { + end = node.previous; + } + } + + /** + * + * Move node to the front of the list. + */ + public void setHead(Node node) { + node.next = head; + node.previous = null; + if (head != null) { + head.previous = node; + } + head = node; + if (end == null) { + end = head; + } + } + + /** + * Set user account + */ + public void set(String userId, UserAccount userAccount) { + if (cache.containsKey(userId)) { + Node old = cache.get(userId); + old.userAccount = userAccount; + remove(old); + setHead(old); + } else { + Node newNode = new Node(userId, userAccount); + if (cache.size() >= capacity) { + System.out.println("# Cache is FULL! Removing " + end.userId + " from cache..."); + cache.remove(end.userId); // remove LRU data from cache. + remove(end); + setHead(newNode); + } else { + setHead(newNode); + } + cache.put(userId, newNode); + } + } + + public boolean contains(String userId) { + return cache.containsKey(userId); + } + + /** + * Invalidate cache for user + */ + public void invalidate(String userId) { + System.out.println("# " + userId + " has been updated! Removing older version from cache..."); + Node toBeRemoved = cache.get(userId); + remove(toBeRemoved); + cache.remove(userId); + } + + public boolean isFull() { + return cache.size() >= capacity; + } + + public UserAccount getLruData() { + return end.userAccount; + } + + /** + * Clear cache + */ + public void clear() { + head = null; + end = null; + cache.clear(); + } + + /** + * + * Returns cache data in list form. + */ + public List getCacheDataInListForm() { + ArrayList listOfCacheData = new ArrayList<>(); + Node temp = head; + while (temp != null) { + listOfCacheData.add(temp.userAccount); + temp = temp.next; + } + return listOfCacheData; + } + + /** + * Set cache capacity + */ + public void setCapacity(int newCapacity) { + if (capacity > newCapacity) { + clear(); // Behavior can be modified to accommodate for decrease in cache size. For now, we'll + // just clear the cache. + } else { + this.capacity = newCapacity; + } + } +} diff --git a/caching/src/main/java/com/iluwatar/caching/UserAccount.java b/caching/src/main/java/com/iluwatar/caching/UserAccount.java new file mode 100644 index 000000000..e2444ad72 --- /dev/null +++ b/caching/src/main/java/com/iluwatar/caching/UserAccount.java @@ -0,0 +1,72 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.caching; + +/** + * + * Entity class (stored in cache and DB) used in the application. + * + */ +public class UserAccount { + private String userId; + private String userName; + private String additionalInfo; + + /** + * Constructor + */ + public UserAccount(String userId, String userName, String additionalInfo) { + this.userId = userId; + this.userName = userName; + this.additionalInfo = additionalInfo; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getAdditionalInfo() { + return additionalInfo; + } + + public void setAdditionalInfo(String additionalInfo) { + this.additionalInfo = additionalInfo; + } + + @Override + public String toString() { + return userId + ", " + userName + ", " + additionalInfo; + } +} diff --git a/caching/src/test/java/com/iluwatar/caching/AppTest.java b/caching/src/test/java/com/iluwatar/caching/AppTest.java new file mode 100644 index 000000000..90ed1c275 --- /dev/null +++ b/caching/src/test/java/com/iluwatar/caching/AppTest.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.caching; + +import org.junit.Test; + +import java.io.IOException; + +/** + * Tests that Caching example runs without errors. + */ +public class AppTest { + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } +} diff --git a/caching/src/test/java/com/iluwatar/caching/CachingTest.java b/caching/src/test/java/com/iluwatar/caching/CachingTest.java new file mode 100644 index 000000000..19262a3b6 --- /dev/null +++ b/caching/src/test/java/com/iluwatar/caching/CachingTest.java @@ -0,0 +1,63 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.caching; + +import org.junit.Before; +import org.junit.Test; + +/** + * + * Application test + * + */ +public class CachingTest { + App app; + + /** + * Setup of application test includes: initializing DB connection and cache size/capacity. + */ + @Before + public void setUp() { + AppManager.initDb(false); // VirtualDB (instead of MongoDB) was used in running the JUnit tests + // to avoid Maven compilation errors. Set flag to true to run the + // tests with MongoDB (provided that MongoDB is installed and socket + // connection is open). + AppManager.initCacheCapacity(3); + app = new App(); + } + + @Test + public void testReadAndWriteThroughStrategy() { + app.useReadAndWriteThroughStrategy(); + } + + @Test + public void testReadThroughAndWriteAroundStrategy() { + app.useReadThroughAndWriteAroundStrategy(); + } + + @Test + public void testReadThroughAndWriteBehindStrategy() { + app.useReadThroughAndWriteBehindStrategy(); + } +} diff --git a/callback/README.md b/callback/README.md new file mode 100644 index 000000000..a408fd5e0 --- /dev/null +++ b/callback/README.md @@ -0,0 +1,28 @@ +--- +layout: pattern +title: Callback +folder: callback +permalink: /patterns/callback/ +categories: Other +tags: + - Java + - Difficulty-Beginner + - Functional + - Idiom +--- + +## Intent +Callback is a piece of executable code that is passed as an +argument to other code, which is expected to call back (execute) the argument +at some convenient time. + +![alt text](./etc/callback.png "Callback") + +## Applicability +Use the Callback pattern when + +* when some arbitrary synchronous or asynchronous action must be performed after execution of some defined activity. + +## Real world examples + +* [CyclicBarrier](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html#CyclicBarrier%28int,%20java.lang.Runnable%29) constructor can accept callback that will be triggered every time when barrier is tripped. diff --git a/callback/pom.xml b/callback/pom.xml index 862fb53a1..4fe17d143 100644 --- a/callback/pom.xml +++ b/callback/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT callback diff --git a/callback/src/main/java/com/iluwatar/callback/App.java b/callback/src/main/java/com/iluwatar/callback/App.java index 546a6b507..bc1b2890e 100644 --- a/callback/src/main/java/com/iluwatar/callback/App.java +++ b/callback/src/main/java/com/iluwatar/callback/App.java @@ -1,19 +1,47 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.callback; /** - * Callback pattern is more native for functional languages where function is treated as first-class citizen. - * Prior to Java8 can be simulated using simple (alike command) interfaces. + * + * Callback pattern is more native for functional languages where functions are treated as + * first-class citizens. Prior to Java 8 callbacks can be simulated using simple (alike command) + * interfaces. + * */ public class App { - public static void main(String[] args) { - Task task = new SimpleTask(); - Callback callback = new Callback() { - @Override - public void call() { - System.out.println("I'm done now."); - } - }; - task.executeWith(callback); - } + /** + * Program entry point + */ + public static void main(String[] args) { + Task task = new SimpleTask(); + Callback callback = new Callback() { + @Override + public void call() { + System.out.println("I'm done now."); + } + }; + task.executeWith(callback); + } } diff --git a/callback/src/main/java/com/iluwatar/callback/Callback.java b/callback/src/main/java/com/iluwatar/callback/Callback.java index 9e757e218..78932a24e 100644 --- a/callback/src/main/java/com/iluwatar/callback/Callback.java +++ b/callback/src/main/java/com/iluwatar/callback/Callback.java @@ -1,9 +1,33 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.callback; /** + * * Callback interface + * */ public interface Callback { - public void call(); + void call(); } diff --git a/callback/src/main/java/com/iluwatar/callback/LambdasApp.java b/callback/src/main/java/com/iluwatar/callback/LambdasApp.java new file mode 100644 index 000000000..78ca63e0b --- /dev/null +++ b/callback/src/main/java/com/iluwatar/callback/LambdasApp.java @@ -0,0 +1,41 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.callback; + +/** + * + * This example generates the exact same output as {@link App} however the callback has been + * defined as a Lambdas expression. + * + */ +public class LambdasApp { + + /** + * Program entry point + */ + public static void main(String[] args) { + Task task = new SimpleTask(); + Callback c = () -> System.out.println("I'm done now."); + task.executeWith(c); + } +} diff --git a/callback/src/main/java/com/iluwatar/callback/SimpleTask.java b/callback/src/main/java/com/iluwatar/callback/SimpleTask.java index f8a27ba10..2a7385607 100644 --- a/callback/src/main/java/com/iluwatar/callback/SimpleTask.java +++ b/callback/src/main/java/com/iluwatar/callback/SimpleTask.java @@ -1,13 +1,36 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.callback; /** + * * Implementation of task that need to be executed + * */ public class SimpleTask extends Task { - @Override - public void execute() { - System.out.println("Perform some important activity."); - } - + @Override + public void execute() { + System.out.println("Perform some important activity and after call the callback method."); + } } diff --git a/callback/src/main/java/com/iluwatar/callback/Task.java b/callback/src/main/java/com/iluwatar/callback/Task.java index df7bf8ae8..9580c91cb 100644 --- a/callback/src/main/java/com/iluwatar/callback/Task.java +++ b/callback/src/main/java/com/iluwatar/callback/Task.java @@ -1,16 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.callback; /** + * * Template-method class for callback hook execution + * */ public abstract class Task { - public final void executeWith(Callback callback) { - execute(); - if (callback != null) { - callback.call(); - } - } + /** + * Execute with callback + */ + public final void executeWith(Callback callback) { + execute(); + if (callback != null) { + callback.call(); + } + } - public abstract void execute(); + public abstract void execute(); } diff --git a/callback/src/test/java/com/iluwatar/callback/AppTest.java b/callback/src/test/java/com/iluwatar/callback/AppTest.java index 5d7febcee..b7ab3fe75 100644 --- a/callback/src/test/java/com/iluwatar/callback/AppTest.java +++ b/callback/src/test/java/com/iluwatar/callback/AppTest.java @@ -1,14 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.callback; import org.junit.Test; -import com.iluwatar.callback.App; +import java.io.IOException; +/** + * Tests that Callback example runs without errors. + */ public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } } diff --git a/callback/src/test/java/com/iluwatar/callback/CallbackTest.java b/callback/src/test/java/com/iluwatar/callback/CallbackTest.java new file mode 100644 index 000000000..b48ac36ff --- /dev/null +++ b/callback/src/test/java/com/iluwatar/callback/CallbackTest.java @@ -0,0 +1,79 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.callback; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Add a field as a counter. Every time the callback method is called increment this field. Unit + * test checks that the field is being incremented. + * + * Could be done with mock objects as well where the call method call is verified. + */ +public class CallbackTest { + + private Integer callingCount = 0; + + @Test + public void test() { + Callback callback = new Callback() { + @Override + public void call() { + callingCount++; + } + }; + + Task task = new SimpleTask(); + + assertEquals("Initial calling count of 0", new Integer(0), callingCount); + + task.executeWith(callback); + + assertEquals("Callback called once", new Integer(1), callingCount); + + task.executeWith(callback); + + assertEquals("Callback called twice", new Integer(2), callingCount); + + } + + @Test + public void testWithLambdasExample() { + Callback callback = () -> callingCount++; + + Task task = new SimpleTask(); + + assertEquals("Initial calling count of 0", new Integer(0), callingCount); + + task.executeWith(callback); + + assertEquals("Callback called once", new Integer(1), callingCount); + + task.executeWith(callback); + + assertEquals("Callback called twice", new Integer(2), callingCount); + + } +} diff --git a/chain/README.md b/chain/README.md new file mode 100644 index 000000000..ef18f6f64 --- /dev/null +++ b/chain/README.md @@ -0,0 +1,34 @@ +--- +layout: pattern +title: Chain of responsibility +folder: chain +permalink: /patterns/chain/ +categories: Behavioral +tags: + - Java + - Gang Of Four + - Difficulty-Intermediate +--- + +## Intent +Avoid coupling the sender of a request to its receiver by giving +more than one object a chance to handle the request. Chain the receiving +objects and pass the request along the chain until an object handles it. + +![alt text](./etc/chain_1.png "Chain of Responsibility") + +## Applicability +Use Chain of Responsibility when + +* more than one object may handle a request, and the handler isn't known a priori. The handler should be ascertained automatically +* you want to issue a request to one of several objects without specifying the receiver explicitly +* the set of objects that can handle a request should be specified dynamically + +## Real world examples + +* [java.util.logging.Logger#log()](http://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#log%28java.util.logging.Level,%20java.lang.String%29) +* [Apache Commons Chain](https://commons.apache.org/proper/commons-chain/index.html) + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/chain/pom.xml b/chain/pom.xml index 3e960293a..ee3b92401 100644 --- a/chain/pom.xml +++ b/chain/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT chain diff --git a/chain/src/main/java/com/iluwatar/chain/App.java b/chain/src/main/java/com/iluwatar/chain/App.java index 21d206696..cd6a200de 100644 --- a/chain/src/main/java/com/iluwatar/chain/App.java +++ b/chain/src/main/java/com/iluwatar/chain/App.java @@ -1,22 +1,53 @@ -package com.iluwatar.chain; - -/** - * - * Chain of Responsibility organizes request handlers (RequestHandler) into a - * chain where each handler has a chance to act on the request on its turn. In - * this example the king (OrcKing) makes requests and the military orcs - * (OrcCommander, OrcOfficer, OrcSoldier) form the handler chain. - * - */ -public class App { - - public static void main(String[] args) { - - OrcKing king = new OrcKing(); - king.makeRequest(new Request(RequestType.DEFEND_CASTLE, "defend castle")); - king.makeRequest(new Request(RequestType.TORTURE_PRISONER, - "torture prisoner")); - king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax")); - - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.chain; + +/** + * + * The Chain of Responsibility pattern is a design pattern consisting of command objects and a + * series of processing objects. Each processing object contains logic that defines the types of + * command objects that it can handle; the rest are passed to the next processing object in the + * chain. A mechanism also exists for adding new processing objects to the end of this chain. + *

+ * In this example we organize the request handlers ({@link RequestHandler}) into a chain where each + * handler has a chance to act on the request on its turn. Here the king ({@link OrcKing}) makes + * requests and the military orcs ({@link OrcCommander}, {@link OrcOfficer}, {@link OrcSoldier}) + * form the handler chain. + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + + OrcKing king = new OrcKing(); + king.makeRequest(new Request(RequestType.DEFEND_CASTLE, "defend castle")); + king.makeRequest(new Request(RequestType.TORTURE_PRISONER, "torture prisoner")); + king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax")); + + } +} diff --git a/chain/src/main/java/com/iluwatar/chain/OrcCommander.java b/chain/src/main/java/com/iluwatar/chain/OrcCommander.java index 1f5f4de86..a1f2bf284 100644 --- a/chain/src/main/java/com/iluwatar/chain/OrcCommander.java +++ b/chain/src/main/java/com/iluwatar/chain/OrcCommander.java @@ -1,22 +1,50 @@ -package com.iluwatar.chain; - -public class OrcCommander extends RequestHandler { - - public OrcCommander(RequestHandler handler) { - super(handler); - } - - @Override - public void handleRequest(Request req) { - if (req.getRequestType().equals(RequestType.DEFEND_CASTLE)) { - printHandling(req); - } else { - super.handleRequest(req); - } - } - - @Override - public String toString() { - return "Orc commander"; - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.chain; + +/** + * + * OrcCommander + * + */ +public class OrcCommander extends RequestHandler { + + public OrcCommander(RequestHandler handler) { + super(handler); + } + + @Override + public void handleRequest(Request req) { + if (req.getRequestType().equals(RequestType.DEFEND_CASTLE)) { + printHandling(req); + req.markHandled(); + } else { + super.handleRequest(req); + } + } + + @Override + public String toString() { + return "Orc commander"; + } +} diff --git a/chain/src/main/java/com/iluwatar/chain/OrcKing.java b/chain/src/main/java/com/iluwatar/chain/OrcKing.java index afd2616fe..3b13c4a53 100644 --- a/chain/src/main/java/com/iluwatar/chain/OrcKing.java +++ b/chain/src/main/java/com/iluwatar/chain/OrcKing.java @@ -1,24 +1,46 @@ -package com.iluwatar.chain; - -/** - * - * Makes requests that are handled by the chain. - * - */ -public class OrcKing { - - RequestHandler chain; - - public OrcKing() { - buildChain(); - } - - private void buildChain() { - chain = new OrcCommander(new OrcOfficer(new OrcSoldier(null))); - } - - public void makeRequest(Request req) { - chain.handleRequest(req); - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.chain; + +/** + * + * OrcKing makes requests that are handled by the chain. + * + */ +public class OrcKing { + + RequestHandler chain; + + public OrcKing() { + buildChain(); + } + + private void buildChain() { + chain = new OrcCommander(new OrcOfficer(new OrcSoldier(null))); + } + + public void makeRequest(Request req) { + chain.handleRequest(req); + } + +} diff --git a/chain/src/main/java/com/iluwatar/chain/OrcOfficer.java b/chain/src/main/java/com/iluwatar/chain/OrcOfficer.java index c09729cc8..ea35722c1 100644 --- a/chain/src/main/java/com/iluwatar/chain/OrcOfficer.java +++ b/chain/src/main/java/com/iluwatar/chain/OrcOfficer.java @@ -1,23 +1,51 @@ -package com.iluwatar.chain; - -public class OrcOfficer extends RequestHandler { - - public OrcOfficer(RequestHandler handler) { - super(handler); - } - - @Override - public void handleRequest(Request req) { - if (req.getRequestType().equals(RequestType.TORTURE_PRISONER)) { - printHandling(req); - } else { - super.handleRequest(req); - } - } - - @Override - public String toString() { - return "Orc officer"; - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.chain; + +/** + * + * OrcOfficer + * + */ +public class OrcOfficer extends RequestHandler { + + public OrcOfficer(RequestHandler handler) { + super(handler); + } + + @Override + public void handleRequest(Request req) { + if (req.getRequestType().equals(RequestType.TORTURE_PRISONER)) { + printHandling(req); + req.markHandled(); + } else { + super.handleRequest(req); + } + } + + @Override + public String toString() { + return "Orc officer"; + } + +} diff --git a/chain/src/main/java/com/iluwatar/chain/OrcSoldier.java b/chain/src/main/java/com/iluwatar/chain/OrcSoldier.java index f52c7d543..d7601bf4f 100644 --- a/chain/src/main/java/com/iluwatar/chain/OrcSoldier.java +++ b/chain/src/main/java/com/iluwatar/chain/OrcSoldier.java @@ -1,22 +1,50 @@ -package com.iluwatar.chain; - -public class OrcSoldier extends RequestHandler { - - public OrcSoldier(RequestHandler handler) { - super(handler); - } - - @Override - public void handleRequest(Request req) { - if (req.getRequestType().equals(RequestType.COLLECT_TAX)) { - printHandling(req); - } else { - super.handleRequest(req); - } - } - - @Override - public String toString() { - return "Orc soldier"; - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.chain; + +/** + * + * OrcSoldier + * + */ +public class OrcSoldier extends RequestHandler { + + public OrcSoldier(RequestHandler handler) { + super(handler); + } + + @Override + public void handleRequest(Request req) { + if (req.getRequestType().equals(RequestType.COLLECT_TAX)) { + printHandling(req); + req.markHandled(); + } else { + super.handleRequest(req); + } + } + + @Override + public String toString() { + return "Orc soldier"; + } +} diff --git a/chain/src/main/java/com/iluwatar/chain/Request.java b/chain/src/main/java/com/iluwatar/chain/Request.java index ad55522f0..399eddb36 100644 --- a/chain/src/main/java/com/iluwatar/chain/Request.java +++ b/chain/src/main/java/com/iluwatar/chain/Request.java @@ -1,33 +1,100 @@ -package com.iluwatar.chain; - -public class Request { - - private String requestDescription; - private RequestType requestType; - - public Request(RequestType requestType, String requestDescription) { - this.setRequestType(requestType); - this.setRequestDescription(requestDescription); - } - - public String getRequestDescription() { - return requestDescription; - } - - public void setRequestDescription(String requestDescription) { - this.requestDescription = requestDescription; - } - - public RequestType getRequestType() { - return requestType; - } - - public void setRequestType(RequestType requestType) { - this.requestType = requestType; - } - - @Override - public String toString() { - return getRequestDescription(); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.chain; + +import java.util.Objects; + +/** + * Request + */ +public class Request { + + /** + * The type of this request, used by each item in the chain to see if they should or can handle + * this particular request + */ + private final RequestType requestType; + + /** + * A description of the request + */ + private final String requestDescription; + + /** + * Indicates if the request is handled or not. A request can only switch state from unhandled to + * handled, there's no way to 'unhandle' a request + */ + private boolean handled; + + /** + * Create a new request of the given type and accompanied description. + * + * @param requestType The type of request + * @param requestDescription The description of the request + */ + public Request(final RequestType requestType, final String requestDescription) { + this.requestType = Objects.requireNonNull(requestType); + this.requestDescription = Objects.requireNonNull(requestDescription); + } + + /** + * Get a description of the request + * + * @return A human readable description of the request + */ + public String getRequestDescription() { + return requestDescription; + } + + /** + * Get the type of this request, used by each person in the chain of command to see if they should + * or can handle this particular request + * + * @return The request type + */ + public RequestType getRequestType() { + return requestType; + } + + /** + * Mark the request as handled + */ + public void markHandled() { + this.handled = true; + } + + /** + * Indicates if this request is handled or not + * + * @return true when the request is handled, false if not + */ + public boolean isHandled() { + return this.handled; + } + + @Override + public String toString() { + return getRequestDescription(); + } + +} diff --git a/chain/src/main/java/com/iluwatar/chain/RequestHandler.java b/chain/src/main/java/com/iluwatar/chain/RequestHandler.java index dbb8b10a6..78d68e5dc 100644 --- a/chain/src/main/java/com/iluwatar/chain/RequestHandler.java +++ b/chain/src/main/java/com/iluwatar/chain/RequestHandler.java @@ -1,23 +1,53 @@ -package com.iluwatar.chain; - -public abstract class RequestHandler { - - private RequestHandler next; - - public RequestHandler(RequestHandler next) { - this.next = next; - } - - public void handleRequest(Request req) { - if (next != null) { - next.handleRequest(req); - } - } - - protected void printHandling(Request req) { - System.out.println(this + " handling request \"" + req + "\""); - } - - @Override - public abstract String toString(); -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.chain; + +/** + * + * RequestHandler + * + */ +public abstract class RequestHandler { + + private RequestHandler next; + + public RequestHandler(RequestHandler next) { + this.next = next; + } + + /** + * Request handler + */ + public void handleRequest(Request req) { + if (next != null) { + next.handleRequest(req); + } + } + + protected void printHandling(Request req) { + System.out.println(this + " handling request \"" + req + "\""); + } + + @Override + public abstract String toString(); +} diff --git a/chain/src/main/java/com/iluwatar/chain/RequestType.java b/chain/src/main/java/com/iluwatar/chain/RequestType.java index 21727535e..ff6dcff4d 100644 --- a/chain/src/main/java/com/iluwatar/chain/RequestType.java +++ b/chain/src/main/java/com/iluwatar/chain/RequestType.java @@ -1,7 +1,34 @@ -package com.iluwatar.chain; - -public enum RequestType { - - DEFEND_CASTLE, TORTURE_PRISONER, COLLECT_TAX - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.chain; + +/** + * + * RequestType enumeration + * + */ +public enum RequestType { + + DEFEND_CASTLE, TORTURE_PRISONER, COLLECT_TAX + +} diff --git a/chain/src/test/java/com/iluwatar/chain/AppTest.java b/chain/src/test/java/com/iluwatar/chain/AppTest.java index 5edba69bb..93a8a4096 100644 --- a/chain/src/test/java/com/iluwatar/chain/AppTest.java +++ b/chain/src/test/java/com/iluwatar/chain/AppTest.java @@ -1,14 +1,39 @@ -package com.iluwatar.chain; - -import org.junit.Test; - -import com.iluwatar.chain.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.chain; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/chain/src/test/java/com/iluwatar/chain/OrcKingTest.java b/chain/src/test/java/com/iluwatar/chain/OrcKingTest.java new file mode 100644 index 000000000..be3650613 --- /dev/null +++ b/chain/src/test/java/com/iluwatar/chain/OrcKingTest.java @@ -0,0 +1,59 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.chain; + +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +/** + * Date: 12/6/15 - 9:29 PM + * + * @author Jeroen Meulemeester + */ +public class OrcKingTest { + + /** + * All possible requests + */ + private static final Request[] REQUESTS = new Request[]{ + new Request(RequestType.DEFEND_CASTLE, "Don't let the barbarians enter my castle!!"), + new Request(RequestType.TORTURE_PRISONER, "Don't just stand there, tickle him!"), + new Request(RequestType.COLLECT_TAX, "Don't steal, the King hates competition ..."), + }; + + @Test + public void testMakeRequest() throws Exception { + final OrcKing king = new OrcKing(); + + for (final Request request : REQUESTS) { + king.makeRequest(request); + assertTrue( + "Expected all requests from King to be handled, but [" + request + "] was not!", + request.isHandled() + ); + } + + } + +} \ No newline at end of file diff --git a/checkstyle-suppressions.xml b/checkstyle-suppressions.xml new file mode 100644 index 000000000..60de4a697 --- /dev/null +++ b/checkstyle-suppressions.xml @@ -0,0 +1,33 @@ + + + + + + + + + diff --git a/checkstyle.xml b/checkstyle.xml new file mode 100644 index 000000000..19b6d2ec4 --- /dev/null +++ b/checkstyle.xml @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/command/README.md b/command/README.md new file mode 100644 index 000000000..a5478394c --- /dev/null +++ b/command/README.md @@ -0,0 +1,46 @@ +--- +layout: pattern +title: Command +folder: command +permalink: /patterns/command/ +categories: Behavioral +tags: + - Java + - Gang Of Four + - Difficulty-Intermediate + - Functional +--- + +## Also known as +Action, Transaction + +## Intent +Encapsulate a request as an object, thereby letting you +parameterize clients with different requests, queue or log requests, and +support undoable operations. + +![alt text](./etc/command.png "Command") + +## Applicability +Use the Command pattern when you want to + +* parameterize objects by an action to perform. You can express such parameterization in a procedural language with a callback function, that is, a function that's registered somewhere to be called at a later point. Commands are an object-oriented replacement for callbacks. +* specify, queue, and execute requests at different times. A Command object can have a lifetime independent of the original request. If the receiver of a request can be represented in an address space-independent way, then you can transfer a command object for the request to a different process and fulfill the request there +* support undo. The Command's execute operation can store state for reversing its effects in the command itself. The Command interface must have an added Unexecute operation that reverses the effects of a previous call to execute. Executed commands are stored in a history list. Unlimited-level undo and redo is achieved by traversing this list backwards and forwards calling unexecute and execute, respectively +* support logging changes so that they can be reapplied in case of a system crash. By augmenting the Command interface with load and store operations, you can keep a persistent log of changes. Recovering from a crash involves reloading logged commands from disk and re-executing them with the execute operation +* structure a system around high-level operations build on primitive operations. Such a structure is common in information systems that support transactions. A transaction encapsulates a set of changes to data. The Command pattern offers a way to model transactions. Commands have a common interface, letting you invoke all transactions the same way. The pattern also makes it easy to extend the system with new transactions + +## Typical Use Case + +* to keep a history of requests +* implement callback functionality +* implement the undo functionality + +## Real world examples + +* [java.lang.Runnable](http://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html) +* [Netflix Hystrix](https://github.com/Netflix/Hystrix/wiki) + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/command/pom.xml b/command/pom.xml index 53a8e1914..a27a56e54 100644 --- a/command/pom.xml +++ b/command/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT command @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/command/src/main/java/com/iluwatar/command/App.java b/command/src/main/java/com/iluwatar/command/App.java index 6d9df821c..93f9c376a 100644 --- a/command/src/main/java/com/iluwatar/command/App.java +++ b/command/src/main/java/com/iluwatar/command/App.java @@ -1,47 +1,77 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.command; /** * - * In Command pattern actions are objects that can be executed and undone. - * - * Four terms always associated with the command pattern are command, receiver, invoker and client. A command - * object (spell) knows about receiver (target) and invokes a method of the receiver. Values for parameters of - * the receiver method are stored in the command. The receiver then does the work. An invoker object (wizard) - * knows how to execute a command, and optionally does bookkeeping about the command execution. The invoker - * does not know anything about a concrete command, it knows only about command interface. Both an invoker object - * and several command objects are held by a client object (app). The client decides which commands to execute at - * which points. To execute a command, it passes the command object to the invoker object. - * - * In other words, in this example the wizard casts spells on the goblin. The wizard keeps track of the previous - * spells cast, so it is easy to undo them. In addition, the wizard keeps track of the spells undone, so they - * can be redone. + * The Command pattern is a behavioral design pattern in which an object is used to encapsulate all + * information needed to perform an action or trigger an event at a later time. This information + * includes the method name, the object that owns the method and values for the method parameters. + *

+ * Four terms always associated with the command pattern are command, receiver, invoker and client. + * A command object (spell) knows about the receiver (target) and invokes a method of the receiver. + * Values for parameters of the receiver method are stored in the command. The receiver then does + * the work. An invoker object (wizard) knows how to execute a command, and optionally does + * bookkeeping about the command execution. The invoker does not know anything about a concrete + * command, it knows only about command interface. Both an invoker object and several command + * objects are held by a client object (app). The client decides which commands to execute at which + * points. To execute a command, it passes the command object to the invoker object. + *

+ * In other words, in this example the wizard casts spells on the goblin. The wizard keeps track of + * the previous spells cast, so it is easy to undo them. In addition, the wizard keeps track of the + * spells undone, so they can be redone. * * */ public class App { - public static void main(String[] args) { - Wizard wizard = new Wizard(); - Goblin goblin = new Goblin(); + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + Wizard wizard = new Wizard(); + Goblin goblin = new Goblin(); - goblin.printStatus(); + goblin.printStatus(); - wizard.castSpell(new ShrinkSpell(), goblin); - goblin.printStatus(); + wizard.castSpell(new ShrinkSpell(), goblin); + goblin.printStatus(); - wizard.castSpell(new InvisibilitySpell(), goblin); - goblin.printStatus(); + wizard.castSpell(new InvisibilitySpell(), goblin); + goblin.printStatus(); - wizard.undoLastSpell(); - goblin.printStatus(); + wizard.undoLastSpell(); + goblin.printStatus(); - wizard.undoLastSpell(); - goblin.printStatus(); + wizard.undoLastSpell(); + goblin.printStatus(); - wizard.redoLastSpell(); - goblin.printStatus(); + wizard.redoLastSpell(); + goblin.printStatus(); - wizard.redoLastSpell(); - goblin.printStatus(); - } + wizard.redoLastSpell(); + goblin.printStatus(); + } } diff --git a/command/src/main/java/com/iluwatar/command/Command.java b/command/src/main/java/com/iluwatar/command/Command.java index 9ffeed24d..c7b0c43bb 100644 --- a/command/src/main/java/com/iluwatar/command/Command.java +++ b/command/src/main/java/com/iluwatar/command/Command.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.command; /** @@ -7,13 +29,13 @@ package com.iluwatar.command; */ public abstract class Command { - public abstract void execute(Target target); + public abstract void execute(Target target); - public abstract void undo(); + public abstract void undo(); - public abstract void redo(); + public abstract void redo(); - @Override - public abstract String toString(); + @Override + public abstract String toString(); } diff --git a/command/src/main/java/com/iluwatar/command/Goblin.java b/command/src/main/java/com/iluwatar/command/Goblin.java index 7d0804b0c..9e21cdefd 100644 --- a/command/src/main/java/com/iluwatar/command/Goblin.java +++ b/command/src/main/java/com/iluwatar/command/Goblin.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.command; /** @@ -7,14 +29,14 @@ package com.iluwatar.command; */ public class Goblin extends Target { - public Goblin() { - setSize(Size.NORMAL); - setVisibility(Visibility.VISIBLE); - } + public Goblin() { + setSize(Size.NORMAL); + setVisibility(Visibility.VISIBLE); + } - @Override - public String toString() { - return "Goblin"; - } + @Override + public String toString() { + return "Goblin"; + } } diff --git a/command/src/main/java/com/iluwatar/command/InvisibilitySpell.java b/command/src/main/java/com/iluwatar/command/InvisibilitySpell.java index b72c34cc2..9435e7245 100644 --- a/command/src/main/java/com/iluwatar/command/InvisibilitySpell.java +++ b/command/src/main/java/com/iluwatar/command/InvisibilitySpell.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.command; /** @@ -7,30 +29,30 @@ package com.iluwatar.command; */ public class InvisibilitySpell extends Command { - private Target target; + private Target target; - @Override - public void execute(Target target) { - target.setVisibility(Visibility.INVISIBLE); - this.target = target; - } + @Override + public void execute(Target target) { + target.setVisibility(Visibility.INVISIBLE); + this.target = target; + } - @Override - public void undo() { - if (target != null) { - target.setVisibility(Visibility.VISIBLE); - } - } + @Override + public void undo() { + if (target != null) { + target.setVisibility(Visibility.VISIBLE); + } + } - @Override - public void redo() { - if (target != null) { - target.setVisibility(Visibility.INVISIBLE); - } - } + @Override + public void redo() { + if (target != null) { + target.setVisibility(Visibility.INVISIBLE); + } + } - @Override - public String toString() { - return "Invisibility spell"; - } + @Override + public String toString() { + return "Invisibility spell"; + } } diff --git a/command/src/main/java/com/iluwatar/command/ShrinkSpell.java b/command/src/main/java/com/iluwatar/command/ShrinkSpell.java index f36438082..e88447353 100644 --- a/command/src/main/java/com/iluwatar/command/ShrinkSpell.java +++ b/command/src/main/java/com/iluwatar/command/ShrinkSpell.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.command; /** @@ -7,32 +29,32 @@ package com.iluwatar.command; */ public class ShrinkSpell extends Command { - private Size oldSize; - private Target target; + private Size oldSize; + private Target target; - @Override - public void execute(Target target) { - oldSize = target.getSize(); - target.setSize(Size.SMALL); - this.target = target; - } + @Override + public void execute(Target target) { + oldSize = target.getSize(); + target.setSize(Size.SMALL); + this.target = target; + } - @Override - public void undo() { - if (oldSize != null && target != null) { - Size temp = target.getSize(); - target.setSize(oldSize); - oldSize = temp; - } - } + @Override + public void undo() { + if (oldSize != null && target != null) { + Size temp = target.getSize(); + target.setSize(oldSize); + oldSize = temp; + } + } - @Override - public void redo() { - undo(); - } + @Override + public void redo() { + undo(); + } - @Override - public String toString() { - return "Shrink spell"; - } + @Override + public String toString() { + return "Shrink spell"; + } } diff --git a/command/src/main/java/com/iluwatar/command/Size.java b/command/src/main/java/com/iluwatar/command/Size.java index a9c20bd59..d33a19453 100644 --- a/command/src/main/java/com/iluwatar/command/Size.java +++ b/command/src/main/java/com/iluwatar/command/Size.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.command; /** @@ -7,16 +29,16 @@ package com.iluwatar.command; */ public enum Size { - SMALL("small"), NORMAL("normal"), LARGE("large"), UNDEFINED(""); - - private String title; + SMALL("small"), NORMAL("normal"), LARGE("large"), UNDEFINED(""); - Size(String title) { - this.title = title; - } + private String title; - @Override - public String toString() { - return title; - } + Size(String title) { + this.title = title; + } + + @Override + public String toString() { + return title; + } } diff --git a/command/src/main/java/com/iluwatar/command/Target.java b/command/src/main/java/com/iluwatar/command/Target.java index 6ea2681d3..2e49ab663 100644 --- a/command/src/main/java/com/iluwatar/command/Target.java +++ b/command/src/main/java/com/iluwatar/command/Target.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.command; /** @@ -7,32 +29,35 @@ package com.iluwatar.command; */ public abstract class Target { - private Size size; + private Size size; - private Visibility visibility; + private Visibility visibility; - public Size getSize() { - return size; - } + public Size getSize() { + return size; + } - public void setSize(Size size) { - this.size = size; - } + public void setSize(Size size) { + this.size = size; + } - public Visibility getVisibility() { - return visibility; - } + public Visibility getVisibility() { + return visibility; + } - public void setVisibility(Visibility visibility) { - this.visibility = visibility; - } + public void setVisibility(Visibility visibility) { + this.visibility = visibility; + } - @Override - public abstract String toString(); + @Override + public abstract String toString(); - public void printStatus() { - System.out.println(String.format("%s, [size=%s] [visibility=%s]", this, - getSize(), getVisibility())); - System.out.println(); - } + /** + * Print status + */ + public void printStatus() { + System.out.println(String.format("%s, [size=%s] [visibility=%s]", this, getSize(), + getVisibility())); + System.out.println(); + } } diff --git a/command/src/main/java/com/iluwatar/command/Visibility.java b/command/src/main/java/com/iluwatar/command/Visibility.java index 3316ac9e3..4ab079d18 100644 --- a/command/src/main/java/com/iluwatar/command/Visibility.java +++ b/command/src/main/java/com/iluwatar/command/Visibility.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.command; /** @@ -7,16 +29,16 @@ package com.iluwatar.command; */ public enum Visibility { - VISIBLE("visible"), INVISIBLE("invisible"), UNDEFINED(""); + VISIBLE("visible"), INVISIBLE("invisible"), UNDEFINED(""); - private String title; + private String title; - Visibility(String title) { - this.title = title; - } + Visibility(String title) { + this.title = title; + } - @Override - public String toString() { - return title; - } + @Override + public String toString() { + return title; + } } diff --git a/command/src/main/java/com/iluwatar/command/Wizard.java b/command/src/main/java/com/iluwatar/command/Wizard.java index 995b4441a..bd0ef85d4 100644 --- a/command/src/main/java/com/iluwatar/command/Wizard.java +++ b/command/src/main/java/com/iluwatar/command/Wizard.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.command; import java.util.Deque; @@ -10,38 +32,46 @@ import java.util.LinkedList; */ public class Wizard { - private Deque undoStack = new LinkedList<>(); - private Deque redoStack = new LinkedList<>(); + private Deque undoStack = new LinkedList<>(); + private Deque redoStack = new LinkedList<>(); - public Wizard() { - } + public Wizard() {} - public void castSpell(Command command, Target target) { - System.out.println(this + " casts " + command + " at " + target); - command.execute(target); - undoStack.offerLast(command); - } + /** + * Cast spell + */ + public void castSpell(Command command, Target target) { + System.out.println(this + " casts " + command + " at " + target); + command.execute(target); + undoStack.offerLast(command); + } - public void undoLastSpell() { - if (!undoStack.isEmpty()) { - Command previousSpell = undoStack.pollLast(); - redoStack.offerLast(previousSpell); - System.out.println(this + " undoes " + previousSpell); - previousSpell.undo(); - } - } + /** + * Undo last spell + */ + public void undoLastSpell() { + if (!undoStack.isEmpty()) { + Command previousSpell = undoStack.pollLast(); + redoStack.offerLast(previousSpell); + System.out.println(this + " undoes " + previousSpell); + previousSpell.undo(); + } + } - public void redoLastSpell() { - if (!redoStack.isEmpty()) { - Command previousSpell = redoStack.pollLast(); - undoStack.offerLast(previousSpell); - System.out.println(this + " redoes " + previousSpell); - previousSpell.redo(); - } - } + /** + * Redo last spell + */ + public void redoLastSpell() { + if (!redoStack.isEmpty()) { + Command previousSpell = redoStack.pollLast(); + undoStack.offerLast(previousSpell); + System.out.println(this + " redoes " + previousSpell); + previousSpell.redo(); + } + } - @Override - public String toString() { - return "Wizard"; - } + @Override + public String toString() { + return "Wizard"; + } } diff --git a/command/src/test/java/com/iluwatar/command/AppTest.java b/command/src/test/java/com/iluwatar/command/AppTest.java index 454f92b63..560594272 100644 --- a/command/src/test/java/com/iluwatar/command/AppTest.java +++ b/command/src/test/java/com/iluwatar/command/AppTest.java @@ -1,14 +1,38 @@ -package com.iluwatar.command; - -import org.junit.Test; - -import com.iluwatar.command.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.command; + +import org.junit.Test; + +import java.io.IOException; + +/** + * Tests that Command example runs without errors. + */ +public class AppTest { + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } +} diff --git a/command/src/test/java/com/iluwatar/command/CommandTest.java b/command/src/test/java/com/iluwatar/command/CommandTest.java new file mode 100644 index 000000000..8201d50c6 --- /dev/null +++ b/command/src/test/java/com/iluwatar/command/CommandTest.java @@ -0,0 +1,93 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.command; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +/** + * The Command pattern is a behavioral design pattern in which an object is used to encapsulate all + * information needed to perform an action or trigger an event at a later time. This information + * includes the method name, the object that owns the method and values for the method parameters. + * + *

Four terms always associated with the command pattern are command, receiver, invoker and + * client. A command object (spell) knows about the receiver (target) and invokes a method of + * the receiver.Values for parameters of the receiver method are stored in the command. The receiver + * then does the work. An invoker object (wizard) knows how to execute a command, and optionally + * does bookkeeping about the command execution. The invoker does not know anything about a + * concrete command, it knows only about command interface. Both an invoker object and several + * command objects are held by a client object (app). The client decides which commands to execute + * at which points. To execute a command, it passes the command object to the invoker object. + */ +public class CommandTest { + + private static final String GOBLIN = "Goblin"; + + /** + * This test verifies that when the wizard casts spells on the goblin. The wizard keeps track of + * the previous spells cast, so it is easy to undo them. In addition, it also verifies that the + * wizard keeps track of the spells undone, so they can be redone. + */ + @Test + public void testCommand() { + + Wizard wizard = new Wizard(); + Goblin goblin = new Goblin(); + + wizard.castSpell(new ShrinkSpell(), goblin); + verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.VISIBLE); + + wizard.castSpell(new InvisibilitySpell(), goblin); + verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.INVISIBLE); + + wizard.undoLastSpell(); + verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.VISIBLE); + + wizard.undoLastSpell(); + verifyGoblin(goblin, GOBLIN, Size.NORMAL, Visibility.VISIBLE); + + wizard.redoLastSpell(); + verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.VISIBLE); + + wizard.redoLastSpell(); + verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.INVISIBLE); + } + + /** + * This method asserts that the passed goblin object has the name as expectedName, size as + * expectedSize and visibility as expectedVisibility. + * + * @param goblin a goblin object whose state is to be verified against other parameters + * @param expectedName expectedName of the goblin + * @param expectedSize expected size of the goblin + * @param expectedVisibilty exepcted visibility of the goblin + */ + private void verifyGoblin(Goblin goblin, String expectedName, Size expectedSize, + Visibility expectedVisibilty) { + assertEquals("Goblin's name must be same as expectedName", expectedName, goblin.toString()); + assertEquals("Goblin's size must be same as expectedSize", expectedSize, goblin.getSize()); + assertEquals("Goblin's visibility must be same as expectedVisibility", expectedVisibilty, + goblin.getVisibility()); + } +} diff --git a/composite/README.md b/composite/README.md new file mode 100644 index 000000000..8b980292d --- /dev/null +++ b/composite/README.md @@ -0,0 +1,33 @@ +--- +layout: pattern +title: Composite +folder: composite +permalink: /patterns/composite/ +categories: Structural +tags: + - Java + - Gang Of Four + - Difficulty-Intermediate +--- + +## Intent +Compose objects into tree structures to represent part-whole +hierarchies. Composite lets clients treat individual objects and compositions +of objects uniformly. + +![alt text](./etc/composite_1.png "Composite") + +## Applicability +Use the Composite pattern when + +* you want to represent part-whole hierarchies of objects +* you want clients to be able to ignore the difference between compositions of objects and individual objects. Clients will treat all objects in the composite structure uniformly + +## Real world examples + +* [java.awt.Container](http://docs.oracle.com/javase/8/docs/api/java/awt/Container.html) and [java.awt.Component](http://docs.oracle.com/javase/8/docs/api/java/awt/Component.html) +* [Apache Wicket](https://github.com/apache/wicket) component tree, see [Component](https://github.com/apache/wicket/blob/91e154702ab1ff3481ef6cbb04c6044814b7e130/wicket-core/src/main/java/org/apache/wicket/Component.java) and [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java) + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/composite/pom.xml b/composite/pom.xml index d8d7e4233..ace29d8d1 100644 --- a/composite/pom.xml +++ b/composite/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT composite diff --git a/composite/src/main/java/com/iluwatar/composite/App.java b/composite/src/main/java/com/iluwatar/composite/App.java index 6dd62a552..cfe37876f 100644 --- a/composite/src/main/java/com/iluwatar/composite/App.java +++ b/composite/src/main/java/com/iluwatar/composite/App.java @@ -1,25 +1,56 @@ -package com.iluwatar.composite; - -/** - * - * With Composite we can treat tree hierarchies of objects with uniform - * interface (LetterComposite). In this example we have sentences composed of - * words composed of letters. - * - */ -public class App { - - public static void main(String[] args) { - System.out.println("Message from the orcs: "); - - LetterComposite orcMessage = new Messenger().messageFromOrcs(); - orcMessage.print(); - - System.out.println("\n"); - - System.out.println("Message from the elves: "); - - LetterComposite elfMessage = new Messenger().messageFromElves(); - elfMessage.print(); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.composite; + +/** + * The Composite pattern is a partitioning design pattern. The Composite pattern describes that a + * group of objects is to be treated in the same way as a single instance of an object. The intent + * of a composite is to "compose" objects into tree structures to represent part-whole hierarchies. + * Implementing the Composite pattern lets clients treat individual objects and compositions + * uniformly. + *

+ * In this example we have sentences composed of words composed of letters. All of the objects can + * be treated through the same interface ({@link LetterComposite}). + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + System.out.println("Message from the orcs: "); + + LetterComposite orcMessage = new Messenger().messageFromOrcs(); + orcMessage.print(); + + System.out.println("\n"); + + System.out.println("Message from the elves: "); + + LetterComposite elfMessage = new Messenger().messageFromElves(); + elfMessage.print(); + } +} diff --git a/composite/src/main/java/com/iluwatar/composite/Letter.java b/composite/src/main/java/com/iluwatar/composite/Letter.java index 9df8b95f7..d6a4005d2 100644 --- a/composite/src/main/java/com/iluwatar/composite/Letter.java +++ b/composite/src/main/java/com/iluwatar/composite/Letter.java @@ -1,21 +1,47 @@ -package com.iluwatar.composite; - -public class Letter extends LetterComposite { - - private char c; - - public Letter(char c) { - this.c = c; - } - - @Override - protected void printThisBefore() { - System.out.print(c); - } - - @Override - protected void printThisAfter() { - // nop - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.composite; + +/** + * + * Letter + * + */ +public class Letter extends LetterComposite { + + private char c; + + public Letter(char c) { + this.c = c; + } + + @Override + protected void printThisBefore() { + System.out.print(c); + } + + @Override + protected void printThisAfter() { + // nop + } +} diff --git a/composite/src/main/java/com/iluwatar/composite/LetterComposite.java b/composite/src/main/java/com/iluwatar/composite/LetterComposite.java index e58d51b25..940562969 100644 --- a/composite/src/main/java/com/iluwatar/composite/LetterComposite.java +++ b/composite/src/main/java/com/iluwatar/composite/LetterComposite.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.composite; import java.util.ArrayList; @@ -10,25 +32,28 @@ import java.util.List; */ public abstract class LetterComposite { - private List children = new ArrayList(); + private List children = new ArrayList<>(); - public void add(LetterComposite letter) { - children.add(letter); - } + public void add(LetterComposite letter) { + children.add(letter); + } - public int count() { - return children.size(); - } + public int count() { + return children.size(); + } - protected abstract void printThisBefore(); + protected abstract void printThisBefore(); - protected abstract void printThisAfter(); + protected abstract void printThisAfter(); - public void print() { - printThisBefore(); - for (LetterComposite letter : children) { - letter.print(); - } - printThisAfter(); - } + /** + * Print + */ + public void print() { + printThisBefore(); + for (LetterComposite letter : children) { + letter.print(); + } + printThisAfter(); + } } diff --git a/composite/src/main/java/com/iluwatar/composite/Messenger.java b/composite/src/main/java/com/iluwatar/composite/Messenger.java index a849408ca..3b758d680 100644 --- a/composite/src/main/java/com/iluwatar/composite/Messenger.java +++ b/composite/src/main/java/com/iluwatar/composite/Messenger.java @@ -1,53 +1,79 @@ -package com.iluwatar.composite; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -public class Messenger { - - LetterComposite messageFromOrcs() { - - List words = new ArrayList(); - - words.add(new Word(Arrays.asList(new Letter('W'), new Letter('h'), - new Letter('e'), new Letter('r'), new Letter('e')))); - words.add(new Word(Arrays.asList(new Letter('t'), new Letter('h'), - new Letter('e'), new Letter('r'), new Letter('e')))); - words.add(new Word(Arrays.asList(new Letter('i'), new Letter('s')))); - words.add(new Word(Arrays.asList(new Letter('a')))); - words.add(new Word(Arrays.asList(new Letter('w'), new Letter('h'), - new Letter('i'), new Letter('p')))); - words.add(new Word(Arrays.asList(new Letter('t'), new Letter('h'), - new Letter('e'), new Letter('r'), new Letter('e')))); - words.add(new Word(Arrays.asList(new Letter('i'), new Letter('s')))); - words.add(new Word(Arrays.asList(new Letter('a')))); - words.add(new Word(Arrays.asList(new Letter('w'), new Letter('a'), - new Letter('y')))); - - return new Sentence(words); - - } - - LetterComposite messageFromElves() { - - List words = new ArrayList(); - - words.add(new Word(Arrays.asList(new Letter('M'), new Letter('u'), - new Letter('c'), new Letter('h')))); - words.add(new Word(Arrays.asList(new Letter('w'), new Letter('i'), - new Letter('n'), new Letter('d')))); - words.add(new Word(Arrays.asList(new Letter('p'), new Letter('o'), - new Letter('u'), new Letter('r'), new Letter('s')))); - words.add(new Word(Arrays.asList(new Letter('f'), new Letter('r'), - new Letter('o'), new Letter('m')))); - words.add(new Word(Arrays.asList(new Letter('y'), new Letter('o'), - new Letter('u'), new Letter('r')))); - words.add(new Word(Arrays.asList(new Letter('m'), new Letter('o'), - new Letter('u'), new Letter('t'), new Letter('h')))); - - return new Sentence(words); - - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.composite; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * + * Messenger + * + */ +public class Messenger { + + LetterComposite messageFromOrcs() { + + List words = new ArrayList<>(); + + words.add(new Word(Arrays.asList(new Letter('W'), new Letter('h'), new Letter('e'), new Letter( + 'r'), new Letter('e')))); + words.add(new Word(Arrays.asList(new Letter('t'), new Letter('h'), new Letter('e'), new Letter( + 'r'), new Letter('e')))); + words.add(new Word(Arrays.asList(new Letter('i'), new Letter('s')))); + words.add(new Word(Arrays.asList(new Letter('a')))); + words.add(new Word(Arrays.asList(new Letter('w'), new Letter('h'), new Letter('i'), new Letter( + 'p')))); + words.add(new Word(Arrays.asList(new Letter('t'), new Letter('h'), new Letter('e'), new Letter( + 'r'), new Letter('e')))); + words.add(new Word(Arrays.asList(new Letter('i'), new Letter('s')))); + words.add(new Word(Arrays.asList(new Letter('a')))); + words.add(new Word(Arrays.asList(new Letter('w'), new Letter('a'), new Letter('y')))); + + return new Sentence(words); + + } + + LetterComposite messageFromElves() { + + List words = new ArrayList<>(); + + words.add(new Word(Arrays.asList(new Letter('M'), new Letter('u'), new Letter('c'), new Letter( + 'h')))); + words.add(new Word(Arrays.asList(new Letter('w'), new Letter('i'), new Letter('n'), new Letter( + 'd')))); + words.add(new Word(Arrays.asList(new Letter('p'), new Letter('o'), new Letter('u'), new Letter( + 'r'), new Letter('s')))); + words.add(new Word(Arrays.asList(new Letter('f'), new Letter('r'), new Letter('o'), new Letter( + 'm')))); + words.add(new Word(Arrays.asList(new Letter('y'), new Letter('o'), new Letter('u'), new Letter( + 'r')))); + words.add(new Word(Arrays.asList(new Letter('m'), new Letter('o'), new Letter('u'), new Letter( + 't'), new Letter('h')))); + + return new Sentence(words); + + } + +} diff --git a/composite/src/main/java/com/iluwatar/composite/Sentence.java b/composite/src/main/java/com/iluwatar/composite/Sentence.java index c83c520de..eea1d4b1d 100644 --- a/composite/src/main/java/com/iluwatar/composite/Sentence.java +++ b/composite/src/main/java/com/iluwatar/composite/Sentence.java @@ -1,23 +1,52 @@ -package com.iluwatar.composite; - -import java.util.List; - -public class Sentence extends LetterComposite { - - public Sentence(List words) { - for (Word w : words) { - this.add(w); - } - } - - @Override - protected void printThisBefore() { - // nop - } - - @Override - protected void printThisAfter() { - System.out.print("."); - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.composite; + +import java.util.List; + +/** + * + * Sentence + * + */ +public class Sentence extends LetterComposite { + + /** + * Constructor + */ + public Sentence(List words) { + for (Word w : words) { + this.add(w); + } + } + + @Override + protected void printThisBefore() { + // nop + } + + @Override + protected void printThisAfter() { + System.out.print("."); + } +} diff --git a/composite/src/main/java/com/iluwatar/composite/Word.java b/composite/src/main/java/com/iluwatar/composite/Word.java index 393cd2019..819f166dc 100644 --- a/composite/src/main/java/com/iluwatar/composite/Word.java +++ b/composite/src/main/java/com/iluwatar/composite/Word.java @@ -1,23 +1,52 @@ -package com.iluwatar.composite; - -import java.util.List; - -public class Word extends LetterComposite { - - public Word(List letters) { - for (Letter l : letters) { - this.add(l); - } - } - - @Override - protected void printThisBefore() { - System.out.print(" "); - } - - @Override - protected void printThisAfter() { - // nop - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.composite; + +import java.util.List; + +/** + * + * Word + * + */ +public class Word extends LetterComposite { + + /** + * Constructor + */ + public Word(List letters) { + for (Letter l : letters) { + this.add(l); + } + } + + @Override + protected void printThisBefore() { + System.out.print(" "); + } + + @Override + protected void printThisAfter() { + // nop + } +} diff --git a/composite/src/test/java/com/iluwatar/composite/AppTest.java b/composite/src/test/java/com/iluwatar/composite/AppTest.java index 5057efe68..336438a99 100644 --- a/composite/src/test/java/com/iluwatar/composite/AppTest.java +++ b/composite/src/test/java/com/iluwatar/composite/AppTest.java @@ -1,14 +1,39 @@ -package com.iluwatar.composite; - -import org.junit.Test; - -import com.iluwatar.composite.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.composite; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/composite/src/test/java/com/iluwatar/composite/MessengerTest.java b/composite/src/test/java/com/iluwatar/composite/MessengerTest.java new file mode 100644 index 000000000..5f75ea017 --- /dev/null +++ b/composite/src/test/java/com/iluwatar/composite/MessengerTest.java @@ -0,0 +1,112 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.composite; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/11/15 - 8:12 PM + * + * @author Jeroen Meulemeester + */ +public class MessengerTest { + + /** + * The buffer used to capture every write to {@link System#out} + */ + private ByteArrayOutputStream stdOutBuffer = new ByteArrayOutputStream(); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream realStdOut = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + this.stdOutBuffer = new ByteArrayOutputStream(); + System.setOut(new PrintStream(stdOutBuffer)); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(realStdOut); + } + + /** + * Test the message from the orcs + */ + @Test + public void testMessageFromOrcs() { + final Messenger messenger = new Messenger(); + testMessage( + messenger.messageFromOrcs(), + "Where there is a whip there is a way." + ); + } + + /** + * Test the message from the elves + */ + @Test + public void testMessageFromElves() { + final Messenger messenger = new Messenger(); + testMessage( + messenger.messageFromElves(), + "Much wind pours from your mouth." + ); + } + + /** + * Test if the given composed message matches the expected message + * + * @param composedMessage The composed message, received from the messenger + * @param message The expected message + */ + private void testMessage(final LetterComposite composedMessage, final String message) { + // Test is the composed message has the correct number of words + final String[] words = message.split(" "); + assertNotNull(composedMessage); + assertEquals(words.length, composedMessage.count()); + + // Print the message to the mocked stdOut ... + composedMessage.print(); + + // ... and verify if the message matches with the expected one + assertEquals(message, new String(this.stdOutBuffer.toByteArray()).trim()); + } + +} diff --git a/dao/README.md b/dao/README.md new file mode 100644 index 000000000..785a1c362 --- /dev/null +++ b/dao/README.md @@ -0,0 +1,26 @@ +--- +layout: pattern +title: Data Access Object +folder: dao +permalink: /patterns/dao/ +categories: Persistence Tier +tags: + - Java + - Difficulty-Beginner +--- + +## Intent +Object provides an abstract interface to some type of database or +other persistence mechanism. + +![alt text](./etc/dao.png "Data Access Object") + +## Applicability +Use the Data Access Object in any of the following situations + +* when you want to consolidate how the data layer is accessed +* when you want to avoid writing multiple data retrieval/persistence layers + +## Credits + +* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) diff --git a/dao/etc/dao.png b/dao/etc/dao.png index 9fe34b976..452e72ba1 100644 Binary files a/dao/etc/dao.png and b/dao/etc/dao.png differ diff --git a/dao/etc/dao.ucls b/dao/etc/dao.ucls index bf11f18b3..0706837fc 100644 --- a/dao/etc/dao.ucls +++ b/dao/etc/dao.ucls @@ -1,45 +1,71 @@ - - + + - - + + - - - - - - - - - + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + diff --git a/dao/pom.xml b/dao/pom.xml index bfd35c727..f64eff8bc 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -1,18 +1,105 @@ - + + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT dao + junit junit test + + log4j + log4j + + + com.h2database + h2 + + + de.bechte.junit + junit-hierarchicalcontextrunner + + + org.mockito + mockito-core + + + + + + + + + src/main/resources + + + src/main/resources + + log4j.xml + + .. + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.6 + + + log4j.xml + + + + true + + + + + + + diff --git a/dao/src/main/java/com/iluwatar/dao/App.java b/dao/src/main/java/com/iluwatar/dao/App.java index dd96cf807..8a0c06739 100644 --- a/dao/src/main/java/com/iluwatar/dao/App.java +++ b/dao/src/main/java/com/iluwatar/dao/App.java @@ -1,47 +1,135 @@ -package com.iluwatar.dao; - -import java.util.ArrayList; -import java.util.List; - -/** - * - * With the DAO pattern, we can use various method calls to retrieve/add/delete/update data without directly - * interacting with the data. The below example demonstrates basic operations(CRUD): select, add, update, and delete. - */ -public class App { - - public static void main(String[] args) { - - CustomerDaoImpl customerDao = new CustomerDaoImpl(generateSampleCustomers()); - - System.out.println("customerDao.getAllCustomers(): " + customerDao.getAllCustomers()); - System.out.println("customerDao.getCusterById(2): " + customerDao.getCusterById(2)); - - Customer customer = new Customer(4, "Dan", "Danson"); - customerDao.addCustomer(customer); - - System.out.println("customerDao.getAllCustomers(): " + customerDao.getAllCustomers()); - - customer.setFirstName("Daniel"); - customer.setLastName("Danielson"); - customerDao.updateCustomer(customer); - - System.out.println("customerDao.getAllCustomers(): " + customerDao.getAllCustomers()); - - customerDao.deleteCustomer(customer); - - System.out.println("customerDao.getAllCustomers(): " + customerDao.getAllCustomers()); - } - - public static List generateSampleCustomers() { - Customer customer1 = new Customer(1, "Adam", "Adamson"); - Customer customer2 = new Customer(2, "Bob", "Bobson"); - Customer customer3 = new Customer(3, "Carl", "Carlson"); - - List customers = new ArrayList(); - customers.add(customer1); - customers.add(customer2); - customers.add(customer3); - return customers; - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.dao; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +import javax.sql.DataSource; + +import org.apache.log4j.Logger; +import org.h2.jdbcx.JdbcDataSource; + +/** + * Data Access Object (DAO) is an object that provides an abstract interface to some type of + * database or other persistence mechanism. By mapping application calls to the persistence layer, + * DAO provide some specific data operations without exposing details of the database. This + * isolation supports the Single responsibility principle. It separates what data accesses the + * application needs, in terms of domain-specific objects and data types (the public interface of + * the DAO), from how these needs can be satisfied with a specific DBMS. + * + *

With the DAO pattern, we can use various method calls to retrieve/add/delete/update data + * without directly interacting with the data source. The below example demonstrates basic CRUD + * operations: select, add, update, and delete. + * + * + */ +public class App { + private static final String DB_URL = "jdbc:h2:~/dao"; + private static Logger log = Logger.getLogger(App.class); + + /** + * Program entry point. + * + * @param args command line args. + * @throws Exception if any error occurs. + */ + public static void main(final String[] args) throws Exception { + final CustomerDao inMemoryDao = new InMemoryCustomerDao(); + performOperationsUsing(inMemoryDao); + + final DataSource dataSource = createDataSource(); + createSchema(dataSource); + final CustomerDao dbDao = new DbCustomerDao(dataSource); + performOperationsUsing(dbDao); + deleteSchema(dataSource); + } + + private static void deleteSchema(DataSource dataSource) throws SQLException { + try (Connection connection = dataSource.getConnection(); + Statement statement = connection.createStatement()) { + statement.execute(CustomerSchemaSql.DELETE_SCHEMA_SQL); + } + } + + private static void createSchema(DataSource dataSource) throws SQLException { + try (Connection connection = dataSource.getConnection(); + Statement statement = connection.createStatement()) { + statement.execute(CustomerSchemaSql.CREATE_SCHEMA_SQL); + } + } + + private static DataSource createDataSource() { + JdbcDataSource dataSource = new JdbcDataSource(); + dataSource.setURL(DB_URL); + return dataSource; + } + + private static void performOperationsUsing(final CustomerDao customerDao) throws Exception { + addCustomers(customerDao); + log.info("customerDao.getAllCustomers(): "); + try (Stream customerStream = customerDao.getAll()) { + customerStream.forEach((customer) -> log.info(customer)); + } + log.info("customerDao.getCustomerById(2): " + customerDao.getById(2)); + final Customer customer = new Customer(4, "Dan", "Danson"); + customerDao.add(customer); + log.info("customerDao.getAllCustomers(): " + customerDao.getAll()); + customer.setFirstName("Daniel"); + customer.setLastName("Danielson"); + customerDao.update(customer); + log.info("customerDao.getAllCustomers(): "); + try (Stream customerStream = customerDao.getAll()) { + customerStream.forEach((cust) -> log.info(cust)); + } + customerDao.delete(customer); + log.info("customerDao.getAllCustomers(): " + customerDao.getAll()); + } + + private static void addCustomers(CustomerDao customerDao) throws Exception { + for (Customer customer : generateSampleCustomers()) { + customerDao.add(customer); + } + } + + /** + * Generate customers. + * + * @return list of customers. + */ + public static List generateSampleCustomers() { + final Customer customer1 = new Customer(1, "Adam", "Adamson"); + final Customer customer2 = new Customer(2, "Bob", "Bobson"); + final Customer customer3 = new Customer(3, "Carl", "Carlson"); + final List customers = new ArrayList<>(); + customers.add(customer1); + customers.add(customer2); + customers.add(customer3); + return customers; + } +} diff --git a/dao/src/main/java/com/iluwatar/dao/Customer.java b/dao/src/main/java/com/iluwatar/dao/Customer.java index 7900fee67..d6b512dd6 100644 --- a/dao/src/main/java/com/iluwatar/dao/Customer.java +++ b/dao/src/main/java/com/iluwatar/dao/Customer.java @@ -1,64 +1,93 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + package com.iluwatar.dao; +/** + * A customer POJO that represents the data that will be read from the data source. + * + */ public class Customer { - private int id; - private String firstName; - private String lastName; - public Customer(int id, String firstName, String lastName) { - this.id = id; - this.firstName = firstName; - this.lastName = lastName; + private int id; + private String firstName; + private String lastName; + + /** + * Creates an instance of customer. + */ + public Customer(final int id, final String firstName, final String lastName) { + this.id = id; + this.firstName = firstName; + this.lastName = lastName; + } + + public int getId() { + return id; + } + + public void setId(final int id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(final String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(final String lastName) { + this.lastName = lastName; + } + + @Override + public String toString() { + return "Customer{" + "id=" + getId() + ", firstName='" + getFirstName() + '\'' + ", lastName='" + + getLastName() + '\'' + '}'; + } + + @Override + public boolean equals(final Object that) { + boolean isEqual = false; + if (this == that) { + isEqual = true; + } else if (that != null && getClass() == that.getClass()) { + final Customer customer = (Customer) that; + if (getId() == customer.getId()) { + isEqual = true; + } } + return isEqual; + } - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public String getLastName() { - return lastName; - } - - public void setLastName(String lastName) { - this.lastName = lastName; - } - - @Override - public String toString() { - return "Customer{" + - "id=" + id + - ", firstName='" + firstName + '\'' + - ", lastName='" + lastName + '\'' + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Customer customer = (Customer) o; - - if (id != customer.id) return false; - - return true; - } - - @Override - public int hashCode() { - int result = id; - return result; - } -} \ No newline at end of file + @Override + public int hashCode() { + return getId(); + } +} diff --git a/dao/src/main/java/com/iluwatar/dao/CustomerDao.java b/dao/src/main/java/com/iluwatar/dao/CustomerDao.java index f201d945d..059a9e9f7 100644 --- a/dao/src/main/java/com/iluwatar/dao/CustomerDao.java +++ b/dao/src/main/java/com/iluwatar/dao/CustomerDao.java @@ -1,11 +1,81 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + package com.iluwatar.dao; -import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; +/** + * In an application the Data Access Object (DAO) is a part of Data access layer. It is an object + * that provides an interface to some type of persistence mechanism. By mapping application calls + * to the persistence layer, DAO provides some specific data operations without exposing details + * of the database. This isolation supports the Single responsibility principle. It separates what + * data accesses the application needs, in terms of domain-specific objects and data types + * (the public interface of the DAO), from how these needs can be satisfied with a specific DBMS, + * database schema, etc. + * + *

Any change in the way data is stored and retrieved will not change the client code as the + * client will be using interface and need not worry about exact source. + * + * @see InMemoryCustomerDao + * @see DbCustomerDao + */ public interface CustomerDao { - public List getAllCustomers(); - public Customer getCusterById(int id); - public void addCustomer(Customer customer); - public void updateCustomer(Customer customer); - public void deleteCustomer(Customer customer); -} \ No newline at end of file + + /** + * @return all the customers as a stream. The stream may be lazily or eagerly evaluated based + * on the implementation. The stream must be closed after use. + * @throws Exception if any error occurs. + */ + Stream getAll() throws Exception; + + /** + * @param id unique identifier of the customer. + * @return an optional with customer if a customer with unique identifier id + * exists, empty optional otherwise. + * @throws Exception if any error occurs. + */ + Optional getById(int id) throws Exception; + + /** + * @param customer the customer to be added. + * @return true if customer is successfully added, false if customer already exists. + * @throws Exception if any error occurs. + */ + boolean add(Customer customer) throws Exception; + + /** + * @param customer the customer to be updated. + * @return true if customer exists and is successfully updated, false otherwise. + * @throws Exception if any error occurs. + */ + boolean update(Customer customer) throws Exception; + + /** + * @param customer the customer to be deleted. + * @return true if customer exists and is successfully deleted, false otherwise. + * @throws Exception if any error occurs. + */ + boolean delete(Customer customer) throws Exception; +} diff --git a/dao/src/main/java/com/iluwatar/dao/CustomerDaoImpl.java b/dao/src/main/java/com/iluwatar/dao/CustomerDaoImpl.java deleted file mode 100644 index ff5332798..000000000 --- a/dao/src/main/java/com/iluwatar/dao/CustomerDaoImpl.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.iluwatar.dao; - -import java.util.List; - -/** - * The data access object (DAO) is an object that provides an abstract interface to some type of database or other persistence mechanism. - * By mapping application calls to the persistence layer, DAO provide some specific data operations without exposing details of the database. - * This isolation supports the Single responsibility principle. It separates what data accesses the application needs, in terms of - * domain-specific objects and data types (the public interface of the DAO), from how these needs can be satisfied with a specific DBMS, - * database schema, etc. - */ -public class CustomerDaoImpl implements CustomerDao { - - // Represents the DB structure for our example so we don't have to managed it ourselves - // Note: Normally this would be in the form of an actual database and not part of the Dao Impl. - private List customers; - - public CustomerDaoImpl(List customers) { - this.customers = customers; - } - - @Override - public List getAllCustomers() { - return customers; - } - - @Override - public Customer getCusterById(int id) { - for (int i = 0; i < customers.size(); i++) { - if (customers.get(i).getId() == id) { - return customers.get(i); - } - } - // No customer found - return null; - } - - @Override - public void addCustomer(Customer customer) { - customers.add(customer); - } - - - @Override - public void updateCustomer(Customer customer) { - if (customers.contains(customer)) { - customers.set(customers.indexOf(customer), customer); - } - } - - @Override - public void deleteCustomer(Customer customer) { - customers.remove(customer); - } -} \ No newline at end of file diff --git a/dao/src/main/java/com/iluwatar/dao/CustomerSchemaSql.java b/dao/src/main/java/com/iluwatar/dao/CustomerSchemaSql.java new file mode 100644 index 000000000..860826abf --- /dev/null +++ b/dao/src/main/java/com/iluwatar/dao/CustomerSchemaSql.java @@ -0,0 +1,31 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.dao; + +public interface CustomerSchemaSql { + + String CREATE_SCHEMA_SQL = "CREATE TABLE CUSTOMERS (ID NUMBER, FNAME VARCHAR(100), " + + "LNAME VARCHAR(100))"; + + String DELETE_SCHEMA_SQL = "DROP TABLE CUSTOMERS"; +} diff --git a/dao/src/main/java/com/iluwatar/dao/DbCustomerDao.java b/dao/src/main/java/com/iluwatar/dao/DbCustomerDao.java new file mode 100644 index 000000000..622b5b1f0 --- /dev/null +++ b/dao/src/main/java/com/iluwatar/dao/DbCustomerDao.java @@ -0,0 +1,183 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.dao; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Optional; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.Consumer; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import javax.sql.DataSource; + +/** + * An implementation of {@link CustomerDao} that persists customers in RDBMS. + * + */ +public class DbCustomerDao implements CustomerDao { + + private final DataSource dataSource; + + /** + * Creates an instance of {@link DbCustomerDao} which uses provided dataSource + * to store and retrieve customer information. + * + * @param dataSource a non-null dataSource. + */ + public DbCustomerDao(DataSource dataSource) { + this.dataSource = dataSource; + } + + /** + * @return a lazily populated stream of customers. Note the stream returned must be closed to + * free all the acquired resources. The stream keeps an open connection to the database till + * it is complete or is closed manually. + */ + @Override + public Stream getAll() throws Exception { + + Connection connection; + try { + connection = getConnection(); + PreparedStatement statement = connection.prepareStatement("SELECT * FROM CUSTOMERS"); + ResultSet resultSet = statement.executeQuery(); + return StreamSupport.stream(new Spliterators.AbstractSpliterator(Long.MAX_VALUE, + Spliterator.ORDERED) { + + @Override + public boolean tryAdvance(Consumer action) { + try { + if (!resultSet.next()) { + return false; + } + action.accept(createCustomer(resultSet)); + return true; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + }, false).onClose(() -> mutedClose(connection)); + } catch (SQLException e) { + throw new Exception(e.getMessage(), e); + } + } + + private Connection getConnection() throws SQLException { + return dataSource.getConnection(); + } + + private void mutedClose(Connection connection) { + try { + connection.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private Customer createCustomer(ResultSet resultSet) throws SQLException { + return new Customer(resultSet.getInt("ID"), + resultSet.getString("FNAME"), + resultSet.getString("LNAME")); + } + + /** + * {@inheritDoc} + */ + @Override + public Optional getById(int id) throws Exception { + try (Connection connection = getConnection(); + PreparedStatement statement = + connection.prepareStatement("SELECT * FROM CUSTOMERS WHERE ID = ?")) { + + statement.setInt(1, id); + ResultSet resultSet = statement.executeQuery(); + if (resultSet.next()) { + return Optional.of(createCustomer(resultSet)); + } else { + return Optional.empty(); + } + } catch (SQLException ex) { + throw new Exception(ex.getMessage(), ex); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean add(Customer customer) throws Exception { + if (getById(customer.getId()).isPresent()) { + return false; + } + + try (Connection connection = getConnection(); + PreparedStatement statement = + connection.prepareStatement("INSERT INTO CUSTOMERS VALUES (?,?,?)")) { + statement.setInt(1, customer.getId()); + statement.setString(2, customer.getFirstName()); + statement.setString(3, customer.getLastName()); + statement.execute(); + return true; + } catch (SQLException ex) { + throw new Exception(ex.getMessage(), ex); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean update(Customer customer) throws Exception { + try (Connection connection = getConnection(); + PreparedStatement statement = + connection.prepareStatement("UPDATE CUSTOMERS SET FNAME = ?, LNAME = ? WHERE ID = ?")) { + statement.setString(1, customer.getFirstName()); + statement.setString(2, customer.getLastName()); + statement.setInt(3, customer.getId()); + return statement.executeUpdate() > 0; + } catch (SQLException ex) { + throw new Exception(ex.getMessage(), ex); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean delete(Customer customer) throws Exception { + try (Connection connection = getConnection(); + PreparedStatement statement = + connection.prepareStatement("DELETE FROM CUSTOMERS WHERE ID = ?")) { + statement.setInt(1, customer.getId()); + return statement.executeUpdate() > 0; + } catch (SQLException ex) { + throw new Exception(ex.getMessage(), ex); + } + } +} diff --git a/dao/src/main/java/com/iluwatar/dao/InMemoryCustomerDao.java b/dao/src/main/java/com/iluwatar/dao/InMemoryCustomerDao.java new file mode 100644 index 000000000..15c63d1de --- /dev/null +++ b/dao/src/main/java/com/iluwatar/dao/InMemoryCustomerDao.java @@ -0,0 +1,73 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.dao; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +/** + * An in memory implementation of {@link CustomerDao}, which stores the customers in JVM memory + * and data is lost when the application exits. + *
+ * This implementation is useful as temporary database or for testing. + */ +public class InMemoryCustomerDao implements CustomerDao { + + private Map idToCustomer = new HashMap<>(); + + /** + * An eagerly evaluated stream of customers stored in memory. + */ + @Override + public Stream getAll() { + return idToCustomer.values().stream(); + } + + @Override + public Optional getById(final int id) { + return Optional.ofNullable(idToCustomer.get(id)); + } + + @Override + public boolean add(final Customer customer) { + if (getById(customer.getId()).isPresent()) { + return false; + } + + idToCustomer.put(customer.getId(), customer); + return true; + } + + @Override + public boolean update(final Customer customer) { + return idToCustomer.replace(customer.getId(), customer) != null; + } + + @Override + public boolean delete(final Customer customer) { + return idToCustomer.remove(customer.getId()) != null; + } +} diff --git a/dao/src/main/resources/log4j.xml b/dao/src/main/resources/log4j.xml new file mode 100644 index 000000000..b591c17e1 --- /dev/null +++ b/dao/src/main/resources/log4j.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dao/src/test/java/com/iluwatar/dao/AppTest.java b/dao/src/test/java/com/iluwatar/dao/AppTest.java index d45e0ef18..169fc046e 100644 --- a/dao/src/test/java/com/iluwatar/dao/AppTest.java +++ b/dao/src/test/java/com/iluwatar/dao/AppTest.java @@ -1,14 +1,37 @@ -package com.iluwatar.dao; - -import org.junit.Test; - -import com.iluwatar.dao.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.dao; + +import org.junit.Test; + +/** + * Tests that DAO example runs without errors. + */ +public class AppTest { + @Test + public void test() throws Exception { + String[] args = {}; + App.main(args); + } +} diff --git a/dao/src/test/java/com/iluwatar/dao/CustomerTest.java b/dao/src/test/java/com/iluwatar/dao/CustomerTest.java new file mode 100644 index 000000000..6a02fd6be --- /dev/null +++ b/dao/src/test/java/com/iluwatar/dao/CustomerTest.java @@ -0,0 +1,97 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.dao; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import org.junit.Before; +import org.junit.Test; + +public class CustomerTest { + + private Customer customer; + private static final int ID = 1; + private static final String FIRSTNAME = "Winston"; + private static final String LASTNAME = "Churchill"; + + @Before + public void setUp() { + customer = new Customer(ID, FIRSTNAME, LASTNAME); + } + + @Test + public void getAndSetId() { + final int newId = 2; + customer.setId(newId); + assertEquals(newId, customer.getId()); + } + + @Test + public void getAndSetFirstname() { + final String newFirstname = "Bill"; + customer.setFirstName(newFirstname); + assertEquals(newFirstname, customer.getFirstName()); + } + + @Test + public void getAndSetLastname() { + final String newLastname = "Clinton"; + customer.setLastName(newLastname); + assertEquals(newLastname, customer.getLastName()); + } + + @Test + public void notEqualWithDifferentId() { + final int newId = 2; + final Customer otherCustomer = new Customer(newId, FIRSTNAME, LASTNAME); + assertNotEquals(customer, otherCustomer); + assertNotEquals(customer.hashCode(), otherCustomer.hashCode()); + } + + @Test + public void equalsWithSameObjectValues() { + final Customer otherCustomer = new Customer(ID, FIRSTNAME, LASTNAME); + assertEquals(customer, otherCustomer); + assertEquals(customer.hashCode(), otherCustomer.hashCode()); + } + + @Test + public void equalsWithSameObjects() { + assertEquals(customer, customer); + assertEquals(customer.hashCode(), customer.hashCode()); + } + + @Test + public void testToString() { + final StringBuffer buffer = new StringBuffer(); + buffer.append("Customer{id=") + .append("" + customer.getId()) + .append(", firstName='") + .append(customer.getFirstName()) + .append("\', lastName='") + .append(customer.getLastName() + "\'}"); + assertEquals(buffer.toString(), customer.toString()); + } +} diff --git a/dao/src/test/java/com/iluwatar/dao/DbCustomerDaoTest.java b/dao/src/test/java/com/iluwatar/dao/DbCustomerDaoTest.java new file mode 100644 index 000000000..ac69699df --- /dev/null +++ b/dao/src/test/java/com/iluwatar/dao/DbCustomerDaoTest.java @@ -0,0 +1,269 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.dao; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.stream.Stream; + +import javax.sql.DataSource; + +import org.h2.jdbcx.JdbcDataSource; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.mockito.Mockito; + +import de.bechte.junit.runners.context.HierarchicalContextRunner; + +/** + * Tests {@link DbCustomerDao}. + */ +@RunWith(HierarchicalContextRunner.class) +public class DbCustomerDaoTest { + + private static final String DB_URL = "jdbc:h2:~/dao"; + private DbCustomerDao dao; + private Customer existingCustomer = new Customer(1, "Freddy", "Krueger"); + + /** + * Creates customers schema. + * @throws SQLException if there is any error while creating schema. + */ + @Before + public void createSchema() throws SQLException { + try (Connection connection = DriverManager.getConnection(DB_URL); + Statement statement = connection.createStatement()) { + statement.execute(CustomerSchemaSql.CREATE_SCHEMA_SQL); + } + } + + /** + * Represents the scenario where DB connectivity is present. + */ + public class ConnectionSuccess { + + /** + * Setup for connection success scenario. + * @throws Exception if any error occurs. + */ + @Before + public void setUp() throws Exception { + JdbcDataSource dataSource = new JdbcDataSource(); + dataSource.setURL(DB_URL); + dao = new DbCustomerDao(dataSource); + boolean result = dao.add(existingCustomer); + assertTrue(result); + } + + /** + * Represents the scenario when DAO operations are being performed on a non existing customer. + */ + public class NonExistingCustomer { + + @Test + public void addingShouldResultInSuccess() throws Exception { + try (Stream allCustomers = dao.getAll()) { + assumeTrue(allCustomers.count() == 1); + } + + final Customer nonExistingCustomer = new Customer(2, "Robert", "Englund"); + boolean result = dao.add(nonExistingCustomer); + assertTrue(result); + + assertCustomerCountIs(2); + assertEquals(nonExistingCustomer, dao.getById(nonExistingCustomer.getId()).get()); + } + + @Test + public void deletionShouldBeFailureAndNotAffectExistingCustomers() throws Exception { + final Customer nonExistingCustomer = new Customer(2, "Robert", "Englund"); + boolean result = dao.delete(nonExistingCustomer); + + assertFalse(result); + assertCustomerCountIs(1); + } + + @Test + public void updationShouldBeFailureAndNotAffectExistingCustomers() throws Exception { + final int nonExistingId = getNonExistingCustomerId(); + final String newFirstname = "Douglas"; + final String newLastname = "MacArthur"; + final Customer customer = new Customer(nonExistingId, newFirstname, newLastname); + boolean result = dao.update(customer); + + assertFalse(result); + assertFalse(dao.getById(nonExistingId).isPresent()); + } + + @Test + public void retrieveShouldReturnNoCustomer() throws Exception { + assertFalse(dao.getById(getNonExistingCustomerId()).isPresent()); + } + } + + /** + * Represents a scenario where DAO operations are being performed on an already existing + * customer. + * + */ + public class ExistingCustomer { + + @Test + public void addingShouldResultInFailureAndNotAffectExistingCustomers() throws Exception { + Customer existingCustomer = new Customer(1, "Freddy", "Krueger"); + + boolean result = dao.add(existingCustomer); + + assertFalse(result); + assertCustomerCountIs(1); + assertEquals(existingCustomer, dao.getById(existingCustomer.getId()).get()); + } + + @Test + public void deletionShouldBeSuccessAndCustomerShouldBeNonAccessible() throws Exception { + boolean result = dao.delete(existingCustomer); + + assertTrue(result); + assertCustomerCountIs(0); + assertFalse(dao.getById(existingCustomer.getId()).isPresent()); + } + + @Test + public void updationShouldBeSuccessAndAccessingTheSameCustomerShouldReturnUpdatedInformation() throws Exception { + final String newFirstname = "Bernard"; + final String newLastname = "Montgomery"; + final Customer customer = new Customer(existingCustomer.getId(), newFirstname, newLastname); + boolean result = dao.update(customer); + + assertTrue(result); + + final Customer cust = dao.getById(existingCustomer.getId()).get(); + assertEquals(newFirstname, cust.getFirstName()); + assertEquals(newLastname, cust.getLastName()); + } + } + } + + /** + * Represents a scenario where DB connectivity is not present due to network issue, or + * DB service unavailable. + * + */ + public class ConnectivityIssue { + + private static final String EXCEPTION_CAUSE = "Connection not available"; + @Rule public ExpectedException exception = ExpectedException.none(); + + /** + * setup a connection failure scenario. + * @throws SQLException if any error occurs. + */ + @Before + public void setUp() throws SQLException { + dao = new DbCustomerDao(mockedDatasource()); + exception.expect(Exception.class); + exception.expectMessage(EXCEPTION_CAUSE); + } + + private DataSource mockedDatasource() throws SQLException { + DataSource mockedDataSource = mock(DataSource.class); + Connection mockedConnection = mock(Connection.class); + SQLException exception = new SQLException(EXCEPTION_CAUSE); + doThrow(exception).when(mockedConnection).prepareStatement(Mockito.anyString()); + doReturn(mockedConnection).when(mockedDataSource).getConnection(); + return mockedDataSource; + } + + @Test + public void addingACustomerFailsWithExceptionAsFeedbackToClient() throws Exception { + dao.add(new Customer(2, "Bernard", "Montgomery")); + } + + @Test + public void deletingACustomerFailsWithExceptionAsFeedbackToTheClient() throws Exception { + dao.delete(existingCustomer); + } + + @Test + public void updatingACustomerFailsWithFeedbackToTheClient() throws Exception { + final String newFirstname = "Bernard"; + final String newLastname = "Montgomery"; + + dao.update(new Customer(existingCustomer.getId(), newFirstname, newLastname)); + } + + @Test + public void retrievingACustomerByIdFailsWithExceptionAsFeedbackToClient() throws Exception { + dao.getById(existingCustomer.getId()); + } + + @Test + public void retrievingAllCustomersFailsWithExceptionAsFeedbackToClient() throws Exception { + dao.getAll(); + } + + } + + /** + * Delete customer schema for fresh setup per test. + * @throws SQLException if any error occurs. + */ + @After + public void deleteSchema() throws SQLException { + try (Connection connection = DriverManager.getConnection(DB_URL); + Statement statement = connection.createStatement()) { + statement.execute(CustomerSchemaSql.DELETE_SCHEMA_SQL); + } + } + + private void assertCustomerCountIs(int count) throws Exception { + try (Stream allCustomers = dao.getAll()) { + assertTrue(allCustomers.count() == count); + } + } + + + /** + * An arbitrary number which does not correspond to an active Customer id. + * + * @return an int of a customer id which doesn't exist + */ + private int getNonExistingCustomerId() { + return 999; + } +} diff --git a/dao/src/test/java/com/iluwatar/dao/InMemoryCustomerDaoTest.java b/dao/src/test/java/com/iluwatar/dao/InMemoryCustomerDaoTest.java new file mode 100644 index 000000000..65a087b9b --- /dev/null +++ b/dao/src/test/java/com/iluwatar/dao/InMemoryCustomerDaoTest.java @@ -0,0 +1,164 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.dao; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; + +import java.util.Optional; +import java.util.stream.Stream; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import de.bechte.junit.runners.context.HierarchicalContextRunner; + +/** + * Tests {@link InMemoryCustomerDao}. + */ +@RunWith(HierarchicalContextRunner.class) +public class InMemoryCustomerDaoTest { + + private InMemoryCustomerDao dao; + private static final Customer CUSTOMER = new Customer(1, "Freddy", "Krueger"); + + @Before + public void setUp() { + dao = new InMemoryCustomerDao(); + assertTrue(dao.add(CUSTOMER)); + } + + /** + * Represents the scenario when the DAO operations are being performed on a non existent + * customer. + */ + public class NonExistingCustomer { + + @Test + public void addingShouldResultInSuccess() throws Exception { + try (Stream allCustomers = dao.getAll()) { + assumeTrue(allCustomers.count() == 1); + } + + final Customer nonExistingCustomer = new Customer(2, "Robert", "Englund"); + boolean result = dao.add(nonExistingCustomer); + assertTrue(result); + + assertCustomerCountIs(2); + assertEquals(nonExistingCustomer, dao.getById(nonExistingCustomer.getId()).get()); + } + + @Test + public void deletionShouldBeFailureAndNotAffectExistingCustomers() throws Exception { + final Customer nonExistingCustomer = new Customer(2, "Robert", "Englund"); + boolean result = dao.delete(nonExistingCustomer); + + assertFalse(result); + assertCustomerCountIs(1); + } + + @Test + public void updationShouldBeFailureAndNotAffectExistingCustomers() throws Exception { + final int nonExistingId = getNonExistingCustomerId(); + final String newFirstname = "Douglas"; + final String newLastname = "MacArthur"; + final Customer customer = new Customer(nonExistingId, newFirstname, newLastname); + boolean result = dao.update(customer); + + assertFalse(result); + assertFalse(dao.getById(nonExistingId).isPresent()); + } + + @Test + public void retrieveShouldReturnNoCustomer() throws Exception { + assertFalse(dao.getById(getNonExistingCustomerId()).isPresent()); + } + } + + /** + * Represents the scenario when the DAO operations are being performed on an already existing + * customer. + */ + public class ExistingCustomer { + + @Test + public void addingShouldResultInFailureAndNotAffectExistingCustomers() throws Exception { + boolean result = dao.add(CUSTOMER); + + assertFalse(result); + assertCustomerCountIs(1); + assertEquals(CUSTOMER, dao.getById(CUSTOMER.getId()).get()); + } + + @Test + public void deletionShouldBeSuccessAndCustomerShouldBeNonAccessible() throws Exception { + boolean result = dao.delete(CUSTOMER); + + assertTrue(result); + assertCustomerCountIs(0); + assertFalse(dao.getById(CUSTOMER.getId()).isPresent()); + } + + @Test + public void updationShouldBeSuccessAndAccessingTheSameCustomerShouldReturnUpdatedInformation() throws Exception { + final String newFirstname = "Bernard"; + final String newLastname = "Montgomery"; + final Customer customer = new Customer(CUSTOMER.getId(), newFirstname, newLastname); + boolean result = dao.update(customer); + + assertTrue(result); + + final Customer cust = dao.getById(CUSTOMER.getId()).get(); + assertEquals(newFirstname, cust.getFirstName()); + assertEquals(newLastname, cust.getLastName()); + } + + @Test + public void retriveShouldReturnTheCustomer() { + Optional optionalCustomer = dao.getById(CUSTOMER.getId()); + + assertTrue(optionalCustomer.isPresent()); + assertEquals(CUSTOMER, optionalCustomer.get()); + } + } + + /** + * An arbitrary number which does not correspond to an active Customer id. + * + * @return an int of a customer id which doesn't exist + */ + private int getNonExistingCustomerId() { + return 999; + } + + private void assertCustomerCountIs(int count) throws Exception { + try (Stream allCustomers = dao.getAll()) { + assertTrue(allCustomers.count() == count); + } + } +} diff --git a/data-mapper/etc/data-mapper.png b/data-mapper/etc/data-mapper.png new file mode 100644 index 000000000..bcda8054a Binary files /dev/null and b/data-mapper/etc/data-mapper.png differ diff --git a/data-mapper/etc/data-mapper.ucls b/data-mapper/etc/data-mapper.ucls new file mode 100644 index 000000000..2467983ce --- /dev/null +++ b/data-mapper/etc/data-mapper.ucls @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data-mapper/index.md b/data-mapper/index.md new file mode 100644 index 000000000..075e8eece --- /dev/null +++ b/data-mapper/index.md @@ -0,0 +1,25 @@ +--- +layout: pattern +title: Data Mapper +folder: data-mapper +permalink: /patterns/data-mapper/ +categories: Persistence Tier +tags: + - Java + - Difficulty-Beginner +--- + +## Intent +A layer of mappers that moves data between objects and a database while keeping them independent of each other and the mapper itself + +![alt text](./etc/data-mapper.png "Data Mapper") + +## Applicability +Use the Data Mapper in any of the following situations + +* when you want to decouple data objects from DB access layer +* when you want to write multiple data retrieval/persistence implementations + +## Credits + +* [Data Mapper](http://richard.jp.leguen.ca/tutoring/soen343-f2010/tutorials/implementing-data-mapper/) diff --git a/data-mapper/pom.xml b/data-mapper/pom.xml new file mode 100644 index 000000000..7d1b83469 --- /dev/null +++ b/data-mapper/pom.xml @@ -0,0 +1,45 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + data-mapper + + + junit + junit + test + + + log4j + log4j + + + diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/App.java b/data-mapper/src/main/java/com/iluwatar/datamapper/App.java new file mode 100644 index 000000000..5fcd0d9ea --- /dev/null +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/App.java @@ -0,0 +1,77 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.datamapper; + +import java.util.Optional; + +import org.apache.log4j.Logger; + +/** + * The Data Mapper (DM) is a layer of software that separates the in-memory objects from the + * database. Its responsibility is to transfer data between the two and also to isolate them from + * each other. With Data Mapper the in-memory objects needn't know even that there's a database + * present; they need no SQL interface code, and certainly no knowledge of the database schema. (The + * database schema is always ignorant of the objects that use it.) Since it's a form of Mapper , + * Data Mapper itself is even unknown to the domain layer. + *

+ * The below example demonstrates basic CRUD operations: Create, Read, Update, and Delete. + * + */ +public final class App { + + private static Logger log = Logger.getLogger(App.class); + + /** + * Program entry point. + * + * @param args command line args. + */ + public static void main(final String... args) { + + /* Create new data mapper for type 'first' */ + final StudentDataMapper mapper = new StudentDataMapperImpl(); + + /* Create new student */ + Student student = new Student(1, "Adam", 'A'); + + /* Add student in respectibe store */ + mapper.insert(student); + + log.debug("App.main(), student : " + student + ", is inserted"); + + /* Find this student */ + final Optional studentToBeFound = mapper.find(student.getStudentId()); + + log.debug("App.main(), student : " + studentToBeFound + ", is searched"); + + /* Update existing student object */ + student = new Student(student.getStudentId(), "AdamUpdated", 'A'); + + /* Update student in respectibe db */ + mapper.update(student); + + log.debug("App.main(), student : " + student + ", is updated"); + log.debug("App.main(), student : " + student + ", is going to be deleted"); + + /* Delete student in db */ + mapper.delete(student); + } + + private App() {} +} diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/DataMapperException.java b/data-mapper/src/main/java/com/iluwatar/datamapper/DataMapperException.java new file mode 100644 index 000000000..a6995b06d --- /dev/null +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/DataMapperException.java @@ -0,0 +1,42 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.datamapper; + +/** + * Using Runtime Exception for avoiding dependancy on implementation exceptions. This helps in + * decoupling. + * + * @author amit.dixit + * + */ +public final class DataMapperException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** + * Constructs a new runtime exception with the specified detail message. The cause is not + * initialized, and may subsequently be initialized by a call to {@link #initCause}. + * + * @param message the detail message. The detail message is saved for later retrieval by the + * {@link #getMessage()} method. + */ + public DataMapperException(final String message) { + super(message); + } +} diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/Student.java b/data-mapper/src/main/java/com/iluwatar/datamapper/Student.java new file mode 100644 index 000000000..0164533c8 --- /dev/null +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/Student.java @@ -0,0 +1,139 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.datamapper; + + +import java.io.Serializable; + +public final class Student implements Serializable { + + private static final long serialVersionUID = 1L; + + private int studentId; + private String name; + private char grade; + + + /** + * Use this constructor to create a Student with all details + * + * @param studentId as unique student id + * @param name as student name + * @param grade as respective grade of student + */ + public Student(final int studentId, final String name, final char grade) { + super(); + + this.studentId = studentId; + this.name = name; + this.grade = grade; + } + + /** + * + * @return the student id + */ + public int getStudentId() { + return studentId; + } + + /** + * + * @param studentId as unique student id + */ + public void setStudentId(final int studentId) { + this.studentId = studentId; + } + + /** + * + * @return name of student + */ + public String getName() { + return name; + } + + /** + * + * @param name as 'name' of student + */ + public void setName(final String name) { + this.name = name; + } + + /** + * + * @return grade of student + */ + public char getGrade() { + return grade; + } + + /** + * + * @param grade as 'grade of student' + */ + public void setGrade(final char grade) { + this.grade = grade; + } + + /** + * + */ + @Override + public boolean equals(final Object inputObject) { + + boolean isEqual = false; + + /* Check if both objects are same */ + if (this == inputObject) { + + isEqual = true; + } else if (inputObject != null && getClass() == inputObject.getClass()) { + + final Student inputStudent = (Student) inputObject; + + /* If student id matched */ + if (this.getStudentId() == inputStudent.getStudentId()) { + + isEqual = true; + } + } + + return isEqual; + } + + /** + * + */ + @Override + public int hashCode() { + + /* Student id is assumed to be unique */ + return this.getStudentId(); + } + + /** + * + */ + @Override + public String toString() { + return "Student [studentId=" + studentId + ", name=" + name + ", grade=" + grade + "]"; + } +} diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapper.java b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapper.java new file mode 100644 index 000000000..40f0c5c72 --- /dev/null +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapper.java @@ -0,0 +1,32 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.datamapper; + +import java.util.Optional; + +public interface StudentDataMapper { + + Optional find(int studentId); + + void insert(Student student) throws DataMapperException; + + void update(Student student) throws DataMapperException; + + void delete(Student student) throws DataMapperException; +} diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapperImpl.java b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapperImpl.java new file mode 100644 index 000000000..7ecd9e7f8 --- /dev/null +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapperImpl.java @@ -0,0 +1,102 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.datamapper; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public final class StudentDataMapperImpl implements StudentDataMapper { + + /* Note: Normally this would be in the form of an actual database */ + private List students = new ArrayList<>(); + + @Override + public Optional find(int studentId) { + + /* Compare with existing students */ + for (final Student student : this.getStudents()) { + + /* Check if student is found */ + if (student.getStudentId() == studentId) { + + return Optional.of(student); + } + } + + /* Return empty value */ + return Optional.empty(); + } + + @Override + public void update(Student studentToBeUpdated) throws DataMapperException { + + + /* Check with existing students */ + if (this.getStudents().contains(studentToBeUpdated)) { + + /* Get the index of student in list */ + final int index = this.getStudents().indexOf(studentToBeUpdated); + + /* Update the student in list */ + this.getStudents().set(index, studentToBeUpdated); + + } else { + + /* Throw user error after wrapping in a runtime exception */ + throw new DataMapperException("Student [" + studentToBeUpdated.getName() + "] is not found"); + } + } + + @Override + public void insert(Student studentToBeInserted) throws DataMapperException { + + /* Check with existing students */ + if (!this.getStudents().contains(studentToBeInserted)) { + + /* Add student in list */ + this.getStudents().add(studentToBeInserted); + + } else { + + /* Throw user error after wrapping in a runtime exception */ + throw new DataMapperException("Student already [" + studentToBeInserted.getName() + "] exists"); + } + } + + @Override + public void delete(Student studentToBeDeleted) throws DataMapperException { + + /* Check with existing students */ + if (this.getStudents().contains(studentToBeDeleted)) { + + /* Delete the student from list */ + this.getStudents().remove(studentToBeDeleted); + + } else { + + /* Throw user error after wrapping in a runtime exception */ + throw new DataMapperException("Student [" + studentToBeDeleted.getName() + "] is not found"); + } + } + + public List getStudents() { + return this.students; + } +} diff --git a/data-mapper/src/main/resources/log4j.xml b/data-mapper/src/main/resources/log4j.xml new file mode 100644 index 000000000..b591c17e1 --- /dev/null +++ b/data-mapper/src/main/resources/log4j.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data-mapper/src/test/java/com/iluwatar/datamapper/AppTest.java b/data-mapper/src/test/java/com/iluwatar/datamapper/AppTest.java new file mode 100644 index 000000000..f2858100e --- /dev/null +++ b/data-mapper/src/test/java/com/iluwatar/datamapper/AppTest.java @@ -0,0 +1,34 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.datamapper; + +import com.iluwatar.datamapper.App; +import org.junit.Test; + +/** + * Tests that Data-Mapper example runs without errors. + */ +public final class AppTest { + + @Test + public void test() { + final String[] args = {}; + App.main(args); + } +} diff --git a/data-mapper/src/test/java/com/iluwatar/datamapper/DataMapperTest.java b/data-mapper/src/test/java/com/iluwatar/datamapper/DataMapperTest.java new file mode 100644 index 000000000..17f4d3922 --- /dev/null +++ b/data-mapper/src/test/java/com/iluwatar/datamapper/DataMapperTest.java @@ -0,0 +1,73 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.datamapper; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.iluwatar.datamapper.Student; +import com.iluwatar.datamapper.StudentDataMapper; +import com.iluwatar.datamapper.StudentDataMapperImpl; + +/** + * The Data Mapper (DM) is a layer of software that separates the in-memory objects from the + * database. Its responsibility is to transfer data between the two and also to isolate them from + * each other. With Data Mapper the in-memory objects needn't know even that there's a database + * present; they need no SQL interface code, and certainly no knowledge of the database schema. (The + * database schema is always ignorant of the objects that use it.) Since it's a form of Mapper , + * Data Mapper itself is even unknown to the domain layer. + *

+ */ +public class DataMapperTest { + + /** + * This test verify that first data mapper is able to perform all CRUD operations on Student + */ + @Test + public void testFirstDataMapper() { + + /* Create new data mapper of first type */ + final StudentDataMapper mapper = new StudentDataMapperImpl(); + + /* Create new student */ + Student student = new Student(1, "Adam", 'A'); + + /* Add student in respectibe db */ + mapper.insert(student); + + /* Check if student is added in db */ + assertEquals(student.getStudentId(), mapper.find(student.getStudentId()).get().getStudentId()); + + /* Update existing student object */ + student = new Student(student.getStudentId(), "AdamUpdated", 'A'); + + /* Update student in respectibe db */ + mapper.update(student); + + /* Check if student is updated in db */ + assertEquals(mapper.find(student.getStudentId()).get().getName(), "AdamUpdated"); + + /* Delete student in db */ + mapper.delete(student); + + /* Result should be false */ + assertEquals(false, mapper.find(student.getStudentId()).isPresent()); + } +} diff --git a/data-mapper/src/test/java/com/iluwatar/datamapper/StudentTest.java b/data-mapper/src/test/java/com/iluwatar/datamapper/StudentTest.java new file mode 100644 index 000000000..a3c0e46c1 --- /dev/null +++ b/data-mapper/src/test/java/com/iluwatar/datamapper/StudentTest.java @@ -0,0 +1,51 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.datamapper; + +import org.junit.Test; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; + +public final class StudentTest { + + @Test + /** + * This API tests the equality behaviour of Student object + * Object Equality should work as per logic defined in equals method + * + * @throws Exception if any execution error during test + */ + public void testEquality() throws Exception { + + /* Create some students */ + final Student firstStudent = new Student(1, "Adam", 'A'); + final Student secondStudent = new Student(2, "Donald", 'B'); + final Student secondSameStudent = new Student(2, "Donald", 'B'); + final Student firstSameStudent = firstStudent; + + /* Check equals functionality: should return 'true' */ + assertTrue(firstStudent.equals(firstSameStudent)); + + /* Check equals functionality: should return 'false' */ + assertFalse(firstStudent.equals(secondStudent)); + + /* Check equals functionality: should return 'true' */ + assertTrue(secondStudent.equals(secondSameStudent)); + } +} diff --git a/decorator/README.md b/decorator/README.md new file mode 100644 index 000000000..63795114c --- /dev/null +++ b/decorator/README.md @@ -0,0 +1,34 @@ +--- +layout: pattern +title: Decorator +folder: decorator +permalink: /patterns/decorator/ +categories: Structural +tags: + - Java + - Gang Of Four + - Difficulty-Beginner +--- + +## Also known as +Wrapper + +## Intent +Attach additional responsibilities to an object dynamically. +Decorators provide a flexible alternative to subclassing for extending +functionality. + +![alt text](./etc/decorator.png "Decorator") + +## Applicability +Use Decorator + +* to add responsibilities to individual objects dynamically and transparently, that is, without affecting other objects +* for responsibilities that can be withdrawn +* when extension by subclassing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination. Or a class definition may be hidden or otherwise unavailable for subclassing + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) +* [Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions](http://www.amazon.com/Functional-Programming-Java-Harnessing-Expressions/dp/1937785467/ref=sr_1_1) +* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) diff --git a/decorator/etc/decorator.png b/decorator/etc/decorator.png index 1e4bfdac2..47a87b20b 100644 Binary files a/decorator/etc/decorator.png and b/decorator/etc/decorator.png differ diff --git a/decorator/etc/decorator.ucls b/decorator/etc/decorator.ucls index 7adb8c3a6..a5353d4ec 100644 --- a/decorator/etc/decorator.ucls +++ b/decorator/etc/decorator.ucls @@ -1,18 +1,18 @@ - + - + - - + + @@ -21,29 +21,46 @@ - + - + + + + + + + + + + + + - - - - + + + + - - + + + + + + diff --git a/decorator/etc/decorator_1.png b/decorator/etc/decorator_1.png deleted file mode 100644 index 5a7afe2d1..000000000 Binary files a/decorator/etc/decorator_1.png and /dev/null differ diff --git a/decorator/pom.xml b/decorator/pom.xml index ab15cde08..7ba2a4ee8 100644 --- a/decorator/pom.xml +++ b/decorator/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT decorator @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/decorator/src/main/java/com/iluwatar/decorator/App.java b/decorator/src/main/java/com/iluwatar/decorator/App.java index f424f51b9..bdc574fbc 100644 --- a/decorator/src/main/java/com/iluwatar/decorator/App.java +++ b/decorator/src/main/java/com/iluwatar/decorator/App.java @@ -1,29 +1,60 @@ -package com.iluwatar.decorator; - -/** - * - * Decorator pattern is more flexible alternative to subclassing. The decorator - * class implements the same interface as the target and uses composition to - * "decorate" calls to the target. - * - * Using decorator pattern it is possible to change class behavior during - * runtime, as the example shows. - * - */ -public class App { - - public static void main(String[] args) { - - // simple troll - System.out.println("A simple looking troll approaches."); - Hostile troll = new Troll(); - troll.attack(); - troll.fleeBattle(); - - // change the behavior of the simple troll by adding a decorator - System.out.println("\nA smart looking troll surprises you."); - Hostile smart = new SmartTroll(troll); - smart.attack(); - smart.fleeBattle(); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.decorator; + +/** + * + * The Decorator pattern is a more flexible alternative to subclassing. The Decorator class + * implements the same interface as the target and uses composition to "decorate" calls to the + * target. Using the Decorator pattern it is possible to change the behavior of the class during + * runtime. + *

+ * In this example we show how the simple {@link Troll} first attacks and then flees the battle. + * Then we decorate the {@link Troll} with a {@link SmartHostile} and perform the attack again. You + * can see how the behavior changes after the decoration. + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + + // simple troll + System.out.println("A simple looking troll approaches."); + Hostile troll = new Troll(); + troll.attack(); + troll.fleeBattle(); + System.out.printf("Simple troll power %d.\n", troll.getAttackPower()); + + // change the behavior of the simple troll by adding a decorator + System.out.println("\nA smart looking troll surprises you."); + Hostile smart = new SmartHostile(troll); + smart.attack(); + smart.fleeBattle(); + System.out.printf("Smart troll power %d.\n", smart.getAttackPower()); + } +} diff --git a/decorator/src/main/java/com/iluwatar/decorator/Hostile.java b/decorator/src/main/java/com/iluwatar/decorator/Hostile.java index 4df751cca..d3414c9dd 100644 --- a/decorator/src/main/java/com/iluwatar/decorator/Hostile.java +++ b/decorator/src/main/java/com/iluwatar/decorator/Hostile.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.decorator; /** @@ -7,8 +29,10 @@ package com.iluwatar.decorator; */ public interface Hostile { - void attack(); + void attack(); - void fleeBattle(); + int getAttackPower(); + + void fleeBattle(); } diff --git a/decorator/src/main/java/com/iluwatar/decorator/SmartHostile.java b/decorator/src/main/java/com/iluwatar/decorator/SmartHostile.java new file mode 100644 index 000000000..3b4b86276 --- /dev/null +++ b/decorator/src/main/java/com/iluwatar/decorator/SmartHostile.java @@ -0,0 +1,56 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.decorator; + +/** + * SmartHostile is a decorator for {@link Hostile} objects. The calls to the {@link Hostile} interface + * are intercepted and decorated. Finally the calls are delegated to the decorated {@link Hostile} + * object. + * + */ +public class SmartHostile implements Hostile { + + private Hostile decorated; + + public SmartHostile(Hostile decorated) { + this.decorated = decorated; + } + + @Override + public void attack() { + System.out.println("It throws a rock at you!"); + decorated.attack(); + } + + @Override + public int getAttackPower() { + // decorated hostile's power + 20 because it is smart + return decorated.getAttackPower() + 20; + } + + @Override + public void fleeBattle() { + System.out.println("It calls for help!"); + decorated.fleeBattle(); + } +} diff --git a/decorator/src/main/java/com/iluwatar/decorator/SmartTroll.java b/decorator/src/main/java/com/iluwatar/decorator/SmartTroll.java deleted file mode 100644 index 9baa729f3..000000000 --- a/decorator/src/main/java/com/iluwatar/decorator/SmartTroll.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.iluwatar.decorator; - -/** - * SmartTroll is a decorator for Hostile objects. - * The calls to the Hostile interface are intercepted - * and decorated. Finally the calls are delegated - * to the decorated Hostile object. - * - */ -public class SmartTroll implements Hostile { - - private Hostile decorated; - - public SmartTroll(Hostile decorated) { - this.decorated = decorated; - } - - @Override - public void attack() { - System.out.println("The troll throws a rock at you!"); - decorated.attack(); - } - - @Override - public void fleeBattle() { - System.out.println("The troll calls for help!"); - decorated.fleeBattle(); - } - -} diff --git a/decorator/src/main/java/com/iluwatar/decorator/Troll.java b/decorator/src/main/java/com/iluwatar/decorator/Troll.java index 8d6cd0aa5..628adda4b 100644 --- a/decorator/src/main/java/com/iluwatar/decorator/Troll.java +++ b/decorator/src/main/java/com/iluwatar/decorator/Troll.java @@ -1,18 +1,46 @@ -package com.iluwatar.decorator; - -/** - * - * Troll implements Hostile interface directly. - * - */ -public class Troll implements Hostile { - - public void attack() { - System.out.println("The troll swings at you with a club!"); - } - - public void fleeBattle() { - System.out.println("The troll shrieks in horror and runs away!"); - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.decorator; + +/** + * + * Troll implements {@link Hostile} interface directly. + * + */ +public class Troll implements Hostile { + + @Override + public void attack() { + System.out.println("The troll swings at you with a club!"); + } + + @Override + public int getAttackPower() { + return 10; + } + + @Override + public void fleeBattle() { + System.out.println("The troll shrieks in horror and runs away!"); + } +} diff --git a/decorator/src/test/java/com/iluwatar/decorator/AppTest.java b/decorator/src/test/java/com/iluwatar/decorator/AppTest.java index 28776c930..747144c6d 100644 --- a/decorator/src/test/java/com/iluwatar/decorator/AppTest.java +++ b/decorator/src/test/java/com/iluwatar/decorator/AppTest.java @@ -1,14 +1,39 @@ -package com.iluwatar.decorator; - -import org.junit.Test; - -import com.iluwatar.decorator.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.decorator; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/decorator/src/test/java/com/iluwatar/decorator/SmartHostileTest.java b/decorator/src/test/java/com/iluwatar/decorator/SmartHostileTest.java new file mode 100644 index 000000000..6432d4e90 --- /dev/null +++ b/decorator/src/test/java/com/iluwatar/decorator/SmartHostileTest.java @@ -0,0 +1,58 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.decorator; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.*; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +/** + * Date: 12/7/15 - 7:47 PM + * + * @author Jeroen Meulemeester + */ +public class SmartHostileTest { + + @Test + public void testSmartHostile() throws Exception { + // Create a normal troll first, but make sure we can spy on it later on. + final Hostile simpleTroll = spy(new Troll()); + + // Now we want to decorate the troll to make it smarter ... + final Hostile smartTroll = new SmartHostile(simpleTroll); + assertEquals(30, smartTroll.getAttackPower()); + verify(simpleTroll, times(1)).getAttackPower(); + + // Check if the smart troll actions are delegated to the decorated troll + smartTroll.attack(); + verify(simpleTroll, times(1)).attack(); + + smartTroll.fleeBattle(); + verify(simpleTroll, times(1)).fleeBattle(); + verifyNoMoreInteractions(simpleTroll); + + } + +} diff --git a/decorator/src/test/java/com/iluwatar/decorator/TrollTest.java b/decorator/src/test/java/com/iluwatar/decorator/TrollTest.java new file mode 100644 index 000000000..84b0f6d20 --- /dev/null +++ b/decorator/src/test/java/com/iluwatar/decorator/TrollTest.java @@ -0,0 +1,84 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.decorator; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.PrintStream; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +/** + * Date: 12/7/15 - 7:26 PM + * + * @author Jeroen Meulemeester + */ +public class TrollTest { + + /** + * The mocked standard out stream, required since the actions don't have any influence on other + * objects, except for writing to the std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + @Test + public void testTrollActions() throws Exception { + final Troll troll = new Troll(); + assertEquals(10, troll.getAttackPower()); + + troll.attack(); + verify(this.stdOutMock, times(1)).println(eq("The troll swings at you with a club!")); + + troll.fleeBattle(); + verify(this.stdOutMock, times(1)).println(eq("The troll shrieks in horror and runs away!")); + + verifyNoMoreInteractions(this.stdOutMock); + } + +} \ No newline at end of file diff --git a/delegation/README.md b/delegation/README.md new file mode 100644 index 000000000..e5c0c6376 --- /dev/null +++ b/delegation/README.md @@ -0,0 +1,30 @@ +--- +layout: pattern +title: Delegation +folder: delegation +permalink: /patterns/delegation/ +categories: Behavioral +tags: + - Java + - Difficulty-Beginner +--- + +## Also known as +Proxy Pattern + +## Intent +It is a technique where an object expresses certain behavior to the outside but in +reality delegates responsibility for implementing that behaviour to an associated object. + +![alt text](./etc/delegation.png "Delegate") + +## Applicability +Use the Delegate pattern in order to achieve the following + +* Reduce the coupling of methods to their class +* Components that behave identically, but realize that this situation can change in the future. + +## Credits + +* [Delegate Pattern: Wikipedia ](https://en.wikipedia.org/wiki/Delegation_pattern) +* [Proxy Pattern: Wikipedia ](https://en.wikipedia.org/wiki/Proxy_pattern) diff --git a/delegation/etc/delegation.png b/delegation/etc/delegation.png new file mode 100644 index 000000000..375ef4d6b Binary files /dev/null and b/delegation/etc/delegation.png differ diff --git a/delegation/etc/delegation.ucls b/delegation/etc/delegation.ucls new file mode 100644 index 000000000..e3ce08873 --- /dev/null +++ b/delegation/etc/delegation.ucls @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/delegation/pom.xml b/delegation/pom.xml new file mode 100644 index 000000000..2fca225b8 --- /dev/null +++ b/delegation/pom.xml @@ -0,0 +1,51 @@ + + + + + + java-design-patterns + com.iluwatar + 1.13.0-SNAPSHOT + + 4.0.0 + + delegation + + + + junit + junit + test + + + com.github.stefanbirkner + system-rules + test + + + \ No newline at end of file diff --git a/delegation/src/main/java/com/iluwatar/delegation/simple/App.java b/delegation/src/main/java/com/iluwatar/delegation/simple/App.java new file mode 100644 index 000000000..1940e7ae2 --- /dev/null +++ b/delegation/src/main/java/com/iluwatar/delegation/simple/App.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.delegation.simple; + +import com.iluwatar.delegation.simple.printers.CanonPrinter; +import com.iluwatar.delegation.simple.printers.EpsonPrinter; +import com.iluwatar.delegation.simple.printers.HpPrinter; + +/** + * The delegate pattern provides a mechanism to abstract away the implementation and control of the desired action. + * The class being called in this case {@link PrinterController} is not responsible for the actual desired action, + * but is actually delegated to a helper class either {@link CanonPrinter}, {@link EpsonPrinter} or {@link HpPrinter}. + * The consumer does not have or require knowledge of the actual class carrying out the action, only the + * container on which they are calling. + * + * In this example the delegates are {@link EpsonPrinter}, {@link HpPrinter} and {@link CanonPrinter} they all implement + * {@link Printer}. The {@link PrinterController} class also implements {@link Printer}. However neither provide the + * functionality of {@link Printer} by printing to the screen, they actually call upon the instance of {@link Printer} + * that they were instantiated with. Therefore delegating the behaviour to another class. + */ +public class App { + + public static final String MESSAGE_TO_PRINT = "hello world"; + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + PrinterController hpPrinterController = new PrinterController(new HpPrinter()); + PrinterController canonPrinterController = new PrinterController(new CanonPrinter()); + PrinterController epsonPrinterController = new PrinterController(new EpsonPrinter()); + + hpPrinterController.print(MESSAGE_TO_PRINT); + canonPrinterController.print(MESSAGE_TO_PRINT); + epsonPrinterController.print(MESSAGE_TO_PRINT); + } + +} diff --git a/delegation/src/main/java/com/iluwatar/delegation/simple/Printer.java b/delegation/src/main/java/com/iluwatar/delegation/simple/Printer.java new file mode 100644 index 000000000..a25f0fba3 --- /dev/null +++ b/delegation/src/main/java/com/iluwatar/delegation/simple/Printer.java @@ -0,0 +1,45 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.delegation.simple; + +import com.iluwatar.delegation.simple.printers.CanonPrinter; +import com.iluwatar.delegation.simple.printers.EpsonPrinter; +import com.iluwatar.delegation.simple.printers.HpPrinter; + +/** + * Interface that both the Controller and the Delegate will implement. + * + * @see CanonPrinter + * @see EpsonPrinter + * @see HpPrinter + */ +public interface Printer { + + /** + * Method that takes a String to print to the screen. This will be implemented on both the + * controller and the delegate allowing the controller to call the same method on the delegate class. + * + * @param message to be printed to the screen + */ + void print(final String message); +} diff --git a/delegation/src/main/java/com/iluwatar/delegation/simple/PrinterController.java b/delegation/src/main/java/com/iluwatar/delegation/simple/PrinterController.java new file mode 100644 index 000000000..8a4f20dd2 --- /dev/null +++ b/delegation/src/main/java/com/iluwatar/delegation/simple/PrinterController.java @@ -0,0 +1,45 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.delegation.simple; + +public class PrinterController implements Printer { + + private final Printer printer; + + public PrinterController(Printer printer) { + this.printer = printer; + } + + /** + * This method is implemented from {@link Printer} however instead on providing an + * implementation, it instead calls upon the class passed through the constructor. This is the delegate, + * hence the pattern. Therefore meaning that the caller does not care of the implementing class only the owning + * controller. + * + * @param message to be printed to the screen + */ + @Override + public void print(String message) { + printer.print(message); + } +} diff --git a/delegation/src/main/java/com/iluwatar/delegation/simple/printers/CanonPrinter.java b/delegation/src/main/java/com/iluwatar/delegation/simple/printers/CanonPrinter.java new file mode 100644 index 000000000..5e3966d85 --- /dev/null +++ b/delegation/src/main/java/com/iluwatar/delegation/simple/printers/CanonPrinter.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.delegation.simple.printers; + +import com.iluwatar.delegation.simple.Printer; + +/** + * Specialised Implementation of {@link Printer} for a Canon Printer, in + * this case the message to be printed is appended to "Canon Printer : " + * + * @see Printer + */ +public class CanonPrinter implements Printer { + + /** + * {@inheritDoc} + */ + @Override + public void print(String message) { + System.out.print("Canon Printer : " + message); + } + +} diff --git a/delegation/src/main/java/com/iluwatar/delegation/simple/printers/EpsonPrinter.java b/delegation/src/main/java/com/iluwatar/delegation/simple/printers/EpsonPrinter.java new file mode 100644 index 000000000..d37fbf613 --- /dev/null +++ b/delegation/src/main/java/com/iluwatar/delegation/simple/printers/EpsonPrinter.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.delegation.simple.printers; + +import com.iluwatar.delegation.simple.Printer; + +/** + * Specialised Implementation of {@link Printer} for a Epson Printer, in + * this case the message to be printed is appended to "Epson Printer : " + * + * @see Printer + */ +public class EpsonPrinter implements Printer { + + /** + * {@inheritDoc} + */ + @Override + public void print(String message) { + System.out.print("Epson Printer : " + message); + } + +} diff --git a/delegation/src/main/java/com/iluwatar/delegation/simple/printers/HpPrinter.java b/delegation/src/main/java/com/iluwatar/delegation/simple/printers/HpPrinter.java new file mode 100644 index 000000000..f81debf62 --- /dev/null +++ b/delegation/src/main/java/com/iluwatar/delegation/simple/printers/HpPrinter.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.delegation.simple.printers; + +import com.iluwatar.delegation.simple.Printer; + +/** + * Specialised Implementation of {@link Printer} for a HP Printer, in + * this case the message to be printed is appended to "HP Printer : " + * + * @see Printer + */ +public class HpPrinter implements Printer { + + /** + * {@inheritDoc} + */ + @Override + public void print(String message) { + System.out.print("HP Printer : " + message); + } + +} diff --git a/delegation/src/test/java/com/iluwatar/delegation/simple/AppTest.java b/delegation/src/test/java/com/iluwatar/delegation/simple/AppTest.java new file mode 100644 index 000000000..63e469a59 --- /dev/null +++ b/delegation/src/test/java/com/iluwatar/delegation/simple/AppTest.java @@ -0,0 +1,35 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.delegation.simple; + +import org.junit.Test; + +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } + +} diff --git a/delegation/src/test/java/com/iluwatar/delegation/simple/DelegateTest.java b/delegation/src/test/java/com/iluwatar/delegation/simple/DelegateTest.java new file mode 100644 index 000000000..6a19c9a1a --- /dev/null +++ b/delegation/src/test/java/com/iluwatar/delegation/simple/DelegateTest.java @@ -0,0 +1,65 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.delegation.simple; + +import com.iluwatar.delegation.simple.printers.CanonPrinter; +import com.iluwatar.delegation.simple.printers.EpsonPrinter; +import com.iluwatar.delegation.simple.printers.HpPrinter; +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.SystemOutRule; + +import static org.junit.Assert.assertEquals; + +public class DelegateTest { + + private static final String MESSAGE = "Test Message Printed"; + + @Rule + public final SystemOutRule systemOutRule = new SystemOutRule().enableLog(); + + @Test + public void testCanonPrinter() throws Exception { + PrinterController printerController = new PrinterController(new CanonPrinter()); + printerController.print(MESSAGE); + + assertEquals("Canon Printer : Test Message Printed", systemOutRule.getLog()); + } + + @Test + public void testHpPrinter() throws Exception { + PrinterController printerController = new PrinterController(new HpPrinter()); + printerController.print(MESSAGE); + + assertEquals("HP Printer : Test Message Printed", systemOutRule.getLog()); + } + + @Test + public void testEpsonPrinter() throws Exception { + PrinterController printerController = new PrinterController(new EpsonPrinter()); + printerController.print(MESSAGE); + + assertEquals("Epson Printer : Test Message Printed", systemOutRule.getLog()); + } + +} diff --git a/dependency-injection/README.md b/dependency-injection/README.md new file mode 100644 index 000000000..735f589b1 --- /dev/null +++ b/dependency-injection/README.md @@ -0,0 +1,26 @@ +--- +layout: pattern +title: Dependency Injection +folder: dependency-injection +permalink: /patterns/dependency-injection/ +categories: Behavioral +tags: + - Java + - Difficulty-Beginner +--- + +## Intent +Dependency Injection is a software design pattern in which one or +more dependencies (or services) are injected, or passed by reference, into a +dependent object (or client) and are made part of the client's state. The +pattern separates the creation of a client's dependencies from its own +behavior, which allows program designs to be loosely coupled and to follow the +inversion of control and single responsibility principles. + +![alt text](./etc/dependency-injection.png "Dependency Injection") + +## Applicability +Use the Dependency Injection pattern when + +* when you need to remove knowledge of concrete implementation from object +* to enable unit testing of classes in isolation using mock objects or stubs diff --git a/dependency-injection/pom.xml b/dependency-injection/pom.xml index 813030b51..3472da240 100644 --- a/dependency-injection/pom.xml +++ b/dependency-injection/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT dependency-injection @@ -13,6 +37,11 @@ junit junit test + + + org.mockito + mockito-core + test com.google.inject diff --git a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/AdvancedWizard.java b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/AdvancedWizard.java new file mode 100644 index 000000000..4eeaccdea --- /dev/null +++ b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/AdvancedWizard.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.dependency.injection; + +/** + * + * AdvancedWizard implements inversion of control. It depends on abstraction that can be injected + * through its constructor. + * + */ +public class AdvancedWizard implements Wizard { + + private Tobacco tobacco; + + public AdvancedWizard(Tobacco tobacco) { + this.tobacco = tobacco; + } + + @Override + public void smoke() { + tobacco.smoke(this); + } +} diff --git a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/App.java b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/App.java new file mode 100644 index 000000000..faf2a6a43 --- /dev/null +++ b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/App.java @@ -0,0 +1,67 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.dependency.injection; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * + * Dependency Injection pattern deals with how objects handle their dependencies. The pattern + * implements so called inversion of control principle. Inversion of control has two specific rules: + * - High-level modules should not depend on low-level modules. Both should depend on abstractions. + * - Abstractions should not depend on details. Details should depend on abstractions. + *

+ * In this example we show you three different wizards. The first one ({@link SimpleWizard}) is a + * naive implementation violating the inversion of control principle. It depends directly on a + * concrete implementation which cannot be changed. + *

+ * The second wizard ({@link AdvancedWizard}) is more flexible. It does not depend on any concrete + * implementation but abstraction. It utilizes Dependency Injection pattern allowing its + * {@link Tobacco} dependency to be injected through its constructor. This way, handling the + * dependency is no longer the wizard's responsibility. It is resolved outside the wizard class. + *

+ * The third example takes the pattern a step further. It uses Guice framework for Dependency + * Injection. {@link TobaccoModule} binds a concrete implementation to abstraction. Injector is then + * used to create {@link GuiceWizard} object with correct dependencies. + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + SimpleWizard simpleWizard = new SimpleWizard(); + simpleWizard.smoke(); + + AdvancedWizard advancedWizard = new AdvancedWizard(new SecondBreakfastTobacco()); + advancedWizard.smoke(); + + Injector injector = Guice.createInjector(new TobaccoModule()); + GuiceWizard guiceWizard = injector.getInstance(GuiceWizard.class); + guiceWizard.smoke(); + } +} diff --git a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/GuiceWizard.java b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/GuiceWizard.java new file mode 100644 index 000000000..6e3baee5a --- /dev/null +++ b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/GuiceWizard.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.dependency.injection; + +import javax.inject.Inject; + +/** + * + * GuiceWizard implements inversion of control. Its dependencies are injected through its + * constructor by Guice framework. + * + */ +public class GuiceWizard implements Wizard { + + private Tobacco tobacco; + + @Inject + public GuiceWizard(Tobacco tobacco) { + this.tobacco = tobacco; + } + + @Override + public void smoke() { + tobacco.smoke(this); + } +} diff --git a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/OldTobyTobacco.java b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/OldTobyTobacco.java new file mode 100644 index 000000000..523be1d37 --- /dev/null +++ b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/OldTobyTobacco.java @@ -0,0 +1,31 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.dependency.injection; + +/** + * + * OldTobyTobacco concrete {@link Tobacco} implementation + * + */ +public class OldTobyTobacco extends Tobacco { +} diff --git a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/RivendellTobacco.java b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/RivendellTobacco.java new file mode 100644 index 000000000..18691a161 --- /dev/null +++ b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/RivendellTobacco.java @@ -0,0 +1,31 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.dependency.injection; + +/** + * + * RivendellTobacco concrete {@link Tobacco} implementation + * + */ +public class RivendellTobacco extends Tobacco { +} diff --git a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/SecondBreakfastTobacco.java b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/SecondBreakfastTobacco.java new file mode 100644 index 000000000..57565daf0 --- /dev/null +++ b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/SecondBreakfastTobacco.java @@ -0,0 +1,31 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.dependency.injection; + +/** + * + * SecondBreakfastTobacco concrete {@link Tobacco} implementation + * + */ +public class SecondBreakfastTobacco extends Tobacco { +} diff --git a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/SimpleWizard.java b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/SimpleWizard.java new file mode 100644 index 000000000..6928fe7fe --- /dev/null +++ b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/SimpleWizard.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.dependency.injection; + +/** + * + * Naive Wizard implementation violating the inversion of control principle. It should depend on + * abstraction instead. + * + */ +public class SimpleWizard implements Wizard { + + private OldTobyTobacco tobacco = new OldTobyTobacco(); + + public void smoke() { + tobacco.smoke(this); + } +} diff --git a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/Tobacco.java b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/Tobacco.java new file mode 100644 index 000000000..74a564ab5 --- /dev/null +++ b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/Tobacco.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.dependency.injection; + +/** + * + * Tobacco abstraction + * + */ +public abstract class Tobacco { + + public void smoke(Wizard wizard) { + System.out.println(String.format("%s smoking %s", wizard.getClass().getSimpleName(), this + .getClass().getSimpleName())); + } +} diff --git a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/TobaccoModule.java b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/TobaccoModule.java new file mode 100644 index 000000000..b8d4df676 --- /dev/null +++ b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/TobaccoModule.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.dependency.injection; + +import com.google.inject.AbstractModule; + +/** + * + * Guice module for binding certain concrete {@link Tobacco} implementation. + * + */ +public class TobaccoModule extends AbstractModule { + + @Override + protected void configure() { + bind(Tobacco.class).to(RivendellTobacco.class); + } +} diff --git a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/Wizard.java b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/Wizard.java new file mode 100644 index 000000000..a5d4d68e0 --- /dev/null +++ b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/Wizard.java @@ -0,0 +1,34 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.dependency.injection; + +/** + * + * Wizard interface + * + */ +public interface Wizard { + + void smoke(); + +} diff --git a/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/AdvancedWizard.java b/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/AdvancedWizard.java deleted file mode 100644 index 5c4b12eef..000000000 --- a/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/AdvancedWizard.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.iluwatar.dependencyinjection; - -/** - * - * AdvancedWizard implements inversion of control. - * It depends on abstraction that can be injected through - * its constructor. - * - */ -public class AdvancedWizard implements Wizard { - - private Tobacco tobacco; - - public AdvancedWizard(Tobacco tobacco) { - this.tobacco = tobacco; - } - - @Override - public void smoke() { - tobacco.smoke(this); - } -} diff --git a/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/App.java b/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/App.java deleted file mode 100644 index 907eaaa9d..000000000 --- a/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/App.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.iluwatar.dependencyinjection; - -import com.google.inject.Guice; -import com.google.inject.Injector; - -/** - * - * Dependency Injection pattern deals with how objects handle their dependencies. The pattern - * implements so called inversion of control principle. Inversion of control has two specific rules: - * - High-level modules should not depend on low-level modules. Both should depend on abstractions. - * - Abstractions should not depend on details. Details should depend on abstractions. - * - * In this example we show you three different wizards. The first one (SimpleWizard) is a naive - * implementation violating the inversion of control principle. It depends directly on a concrete - * implementation which cannot be changed. - * - * The second wizard (AdvancedWizard) is more flexible. It does not depend on any concrete implementation - * but abstraction. It utilizes Dependency Injection pattern allowing its Tobacco dependency to be - * injected through its constructor. This way, handling the dependency is no longer the wizard's - * responsibility. It is resolved outside the wizard class. - * - * The third example takes the pattern a step further. It uses Guice framework for Dependency Injection. - * TobaccoModule binds a concrete implementation to abstraction. Injector is then used to create - * GuiceWizard object with correct dependencies. - * - */ -public class App { - - public static void main( String[] args ) { - SimpleWizard simpleWizard = new SimpleWizard(); - simpleWizard.smoke(); - - AdvancedWizard advancedWizard = new AdvancedWizard(new SecondBreakfastTobacco()); - advancedWizard.smoke(); - - Injector injector = Guice.createInjector(new TobaccoModule()); - GuiceWizard guiceWizard = injector.getInstance(GuiceWizard.class); - guiceWizard.smoke(); - } -} diff --git a/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/GuiceWizard.java b/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/GuiceWizard.java deleted file mode 100644 index dbbd0a144..000000000 --- a/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/GuiceWizard.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.iluwatar.dependencyinjection; - -import javax.inject.Inject; - -/** - * - * GuiceWizard implements inversion of control. - * Its dependencies are injected through its constructor - * by Guice framework. - * - */ -public class GuiceWizard implements Wizard { - - private Tobacco tobacco; - - @Inject - public GuiceWizard(Tobacco tobacco) { - this.tobacco = tobacco; - } - - @Override - public void smoke() { - tobacco.smoke(this); - } -} diff --git a/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/OldTobyTobacco.java b/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/OldTobyTobacco.java deleted file mode 100644 index 6ebe44033..000000000 --- a/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/OldTobyTobacco.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.iluwatar.dependencyinjection; - -/** - * - * OldTobyTobacco concrete Tobacco implementation - * - */ -public class OldTobyTobacco extends Tobacco { -} diff --git a/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/RivendellTobacco.java b/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/RivendellTobacco.java deleted file mode 100644 index 08bebfac0..000000000 --- a/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/RivendellTobacco.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.iluwatar.dependencyinjection; - -/** - * - * RivendellTobacco concrete Tobacco implementation - * - */ -public class RivendellTobacco extends Tobacco { -} diff --git a/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/SecondBreakfastTobacco.java b/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/SecondBreakfastTobacco.java deleted file mode 100644 index d0831a34a..000000000 --- a/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/SecondBreakfastTobacco.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.iluwatar.dependencyinjection; - -/** - * - * SecondBreakfastTobacco concrete Tobacco implementation - * - */ -public class SecondBreakfastTobacco extends Tobacco { -} diff --git a/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/SimpleWizard.java b/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/SimpleWizard.java deleted file mode 100644 index 18031d840..000000000 --- a/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/SimpleWizard.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.iluwatar.dependencyinjection; - -/** - * - * Naive Wizard implementation violating the inversion of control principle. - * It should depend on abstraction instead. - * - */ -public class SimpleWizard implements Wizard { - - private OldTobyTobacco tobacco = new OldTobyTobacco(); - - public void smoke() { - tobacco.smoke(this); - } -} diff --git a/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/Tobacco.java b/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/Tobacco.java deleted file mode 100644 index a10f648c9..000000000 --- a/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/Tobacco.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.iluwatar.dependencyinjection; - -/** - * - * Tobacco abstraction - * - */ -public abstract class Tobacco { - - public void smoke(Wizard wizard) { - System.out.println(String.format("%s smoking %s", wizard.getClass().getSimpleName(), this.getClass().getSimpleName())); - } -} diff --git a/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/TobaccoModule.java b/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/TobaccoModule.java deleted file mode 100644 index 3a687e8fd..000000000 --- a/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/TobaccoModule.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.iluwatar.dependencyinjection; - -import com.google.inject.AbstractModule; - -/** - * - * Guice module for binding certain concrete Tobacco implementation. - * - */ -public class TobaccoModule extends AbstractModule { - - @Override - protected void configure() { - bind(Tobacco.class).to(RivendellTobacco.class); - } -} diff --git a/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/Wizard.java b/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/Wizard.java deleted file mode 100644 index e133265ca..000000000 --- a/dependency-injection/src/main/java/com/iluwatar/dependencyinjection/Wizard.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.iluwatar.dependencyinjection; - -/** - * - * Wizard interface - * - */ -public interface Wizard { - - void smoke(); - -} diff --git a/dependency-injection/src/test/java/com/iluwatar/dependency/injection/AdvancedWizardTest.java b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/AdvancedWizardTest.java new file mode 100644 index 000000000..d1f5e574c --- /dev/null +++ b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/AdvancedWizardTest.java @@ -0,0 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.dependency.injection; + +import org.junit.Test; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/10/15 - 8:40 PM + * + * @author Jeroen Meulemeester + */ +public class AdvancedWizardTest extends StdOutTest { + + /** + * Test if the {@link AdvancedWizard} smokes whatever instance of {@link Tobacco} is passed to him + * through the constructor parameter + */ + @Test + public void testSmokeEveryThing() throws Exception { + + final Tobacco[] tobaccos = { + new OldTobyTobacco(), new RivendellTobacco(), new SecondBreakfastTobacco() + }; + + for (final Tobacco tobacco : tobaccos) { + final AdvancedWizard advancedWizard = new AdvancedWizard(tobacco); + advancedWizard.smoke(); + + // Verify if the wizard is smoking the correct tobacco ... + verify(getStdOutMock(), times(1)).println("AdvancedWizard smoking " + tobacco.getClass().getSimpleName()); + + // ... and nothing else is happening. + verifyNoMoreInteractions(getStdOutMock()); + } + + } + +} \ No newline at end of file diff --git a/dependency-injection/src/test/java/com/iluwatar/dependency/injection/AppTest.java b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/AppTest.java new file mode 100644 index 000000000..2003933ac --- /dev/null +++ b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/AppTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.dependency.injection; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/dependency-injection/src/test/java/com/iluwatar/dependency/injection/GuiceWizardTest.java b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/GuiceWizardTest.java new file mode 100644 index 000000000..1d3d679df --- /dev/null +++ b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/GuiceWizardTest.java @@ -0,0 +1,100 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.dependency.injection; + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; + +import org.junit.Test; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/10/15 - 8:57 PM + * + * @author Jeroen Meulemeester + */ +public class GuiceWizardTest extends StdOutTest { + + /** + * Test if the {@link GuiceWizard} smokes whatever instance of {@link Tobacco} is passed to him + * through the constructor parameter + */ + @Test + public void testSmokeEveryThingThroughConstructor() throws Exception { + + final Tobacco[] tobaccos = { + new OldTobyTobacco(), new RivendellTobacco(), new SecondBreakfastTobacco() + }; + + for (final Tobacco tobacco : tobaccos) { + final GuiceWizard guiceWizard = new GuiceWizard(tobacco); + guiceWizard.smoke(); + + // Verify if the wizard is smoking the correct tobacco ... + verify(getStdOutMock(), times(1)).println("GuiceWizard smoking " + tobacco.getClass().getSimpleName()); + + // ... and nothing else is happening. + verifyNoMoreInteractions(getStdOutMock()); + } + + } + + /** + * Test if the {@link GuiceWizard} smokes whatever instance of {@link Tobacco} is passed to him + * through the Guice google inject framework + */ + @Test + public void testSmokeEveryThingThroughInjectionFramework() throws Exception { + + @SuppressWarnings("unchecked") + final Class[] tobaccos = new Class[]{ + OldTobyTobacco.class, RivendellTobacco.class, SecondBreakfastTobacco.class + }; + + for (final Class tobaccoClass : tobaccos) { + // Configure the tobacco in the injection framework ... + final Injector injector = Guice.createInjector(new AbstractModule() { + @Override + protected void configure() { + bind(Tobacco.class).to(tobaccoClass); + } + }); + + // ... and create a new wizard with it + final GuiceWizard guiceWizard = injector.getInstance(GuiceWizard.class); + guiceWizard.smoke(); + + // Verify if the wizard is smoking the correct tobacco ... + verify(getStdOutMock(), times(1)).println("GuiceWizard smoking " + tobaccoClass.getSimpleName()); + + // ... and nothing else is happening. + verifyNoMoreInteractions(getStdOutMock()); + } + + } + +} \ No newline at end of file diff --git a/dependency-injection/src/test/java/com/iluwatar/dependency/injection/SimpleWizardTest.java b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/SimpleWizardTest.java new file mode 100644 index 000000000..e5a856e8d --- /dev/null +++ b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/SimpleWizardTest.java @@ -0,0 +1,48 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.dependency.injection; + +import org.junit.Test; + +import static org.mockito.Mockito.*; + +/** + * Date: 12/10/15 - 8:26 PM + * + * @author Jeroen Meulemeester + */ +public class SimpleWizardTest extends StdOutTest { + + /** + * Test if the {@link SimpleWizard} does the only thing it can do: Smoke it's {@link + * OldTobyTobacco} + */ + @Test + public void testSmoke() { + final SimpleWizard simpleWizard = new SimpleWizard(); + simpleWizard.smoke(); + verify(getStdOutMock(), times(1)).println("SimpleWizard smoking OldTobyTobacco"); + verifyNoMoreInteractions(getStdOutMock()); + } + +} \ No newline at end of file diff --git a/dependency-injection/src/test/java/com/iluwatar/dependency/injection/StdOutTest.java b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/StdOutTest.java new file mode 100644 index 000000000..57272c511 --- /dev/null +++ b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/StdOutTest.java @@ -0,0 +1,75 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.dependency.injection; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since the actions of the wizard don't + * have any influence on any other accessible objects, except for writing to std-out using {@link + * System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } +} diff --git a/dependency-injection/src/test/java/com/iluwatar/dependencyinjection/AppTest.java b/dependency-injection/src/test/java/com/iluwatar/dependencyinjection/AppTest.java deleted file mode 100644 index a8944c490..000000000 --- a/dependency-injection/src/test/java/com/iluwatar/dependencyinjection/AppTest.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.iluwatar.dependencyinjection; - -import org.junit.Test; - -import com.iluwatar.dependencyinjection.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} diff --git a/double-checked-locking/README.md b/double-checked-locking/README.md new file mode 100644 index 000000000..da1fdd1a2 --- /dev/null +++ b/double-checked-locking/README.md @@ -0,0 +1,25 @@ +--- +layout: pattern +title: Double Checked Locking +folder: double-checked-locking +permalink: /patterns/double-checked-locking/ +categories: Concurrency +tags: + - Java + - Difficulty-Beginner + - Idiom +--- + +## Intent +Reduce the overhead of acquiring a lock by first testing the +locking criterion (the "lock hint") without actually acquiring the lock. Only +if the locking criterion check indicates that locking is required does the +actual locking logic proceed. + +![alt text](./etc/double_checked_locking_1.png "Double Checked Locking") + +## Applicability +Use the Double Checked Locking pattern when + +* there is a concurrent access in object creation, e.g. singleton, where you want to create single instance of the same class and checking if it's null or not maybe not be enough when there are two or more threads that checks if instance is null or not. +* there is a concurrent access on a method where method's behaviour changes according to the some constraints and these constraint change within this method. diff --git a/double-checked-locking/etc/double-checked-locking.ucls b/double-checked-locking/etc/double-checked-locking.ucls index 43bfbfb78..3ec179228 100644 --- a/double-checked-locking/etc/double-checked-locking.ucls +++ b/double-checked-locking/etc/double-checked-locking.ucls @@ -1,8 +1,8 @@ - - - - + + + + + + diff --git a/double-checked-locking/pom.xml b/double-checked-locking/pom.xml index 8103cf986..239bcf870 100644 --- a/double-checked-locking/pom.xml +++ b/double-checked-locking/pom.xml @@ -1,9 +1,33 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT double-checked-locking @@ -12,5 +36,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/App.java b/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/App.java new file mode 100644 index 000000000..98309e181 --- /dev/null +++ b/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/App.java @@ -0,0 +1,64 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.doublechecked.locking; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * + * Double Checked Locking is a concurrency design pattern used to reduce the overhead of acquiring a + * lock by first testing the locking criterion (the "lock hint") without actually acquiring the + * lock. Only if the locking criterion check indicates that locking is required does the actual + * locking logic proceed. + *

+ * In {@link Inventory} we store the items with a given size. However, we do not store more items + * than the inventory size. To address concurrent access problems we use double checked locking to + * add item to inventory. In this method, the thread which gets the lock first adds the item. + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + final Inventory inventory = new Inventory(1000); + ExecutorService executorService = Executors.newFixedThreadPool(3); + for (int i = 0; i < 3; i++) { + executorService.execute(() -> { + while (inventory.addItem(new Item())) {}; + }); + } + + executorService.shutdown(); + try { + executorService.awaitTermination(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + System.out.println("Error waiting for ExecutorService shutdown"); + } + } +} diff --git a/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/Inventory.java b/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/Inventory.java new file mode 100644 index 000000000..176203a44 --- /dev/null +++ b/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/Inventory.java @@ -0,0 +1,80 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.doublechecked.locking; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * + * Inventory + * + */ +public class Inventory { + + private final int inventorySize; + private final List items; + private final Lock lock; + + /** + * Constructor + */ + public Inventory(int inventorySize) { + this.inventorySize = inventorySize; + this.items = new ArrayList<>(inventorySize); + this.lock = new ReentrantLock(); + } + + /** + * Add item + */ + public boolean addItem(Item item) { + if (items.size() < inventorySize) { + lock.lock(); + try { + if (items.size() < inventorySize) { + items.add(item); + System.out.println(Thread.currentThread() + ": items.size()=" + items.size() + + ", inventorySize=" + inventorySize); + return true; + } + } finally { + lock.unlock(); + } + } + return false; + } + + /** + * Get all the items in the inventory + * + * @return All the items of the inventory, as an unmodifiable list + */ + public final List getItems() { + return Collections.unmodifiableList(items); + } + +} diff --git a/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/Item.java b/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/Item.java new file mode 100644 index 000000000..c805022ab --- /dev/null +++ b/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/Item.java @@ -0,0 +1,31 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.doublechecked.locking; + +/** + * + * Item + * + */ +public class Item { +} diff --git a/double-checked-locking/src/main/java/com/iluwatar/doublecheckedlocking/App.java b/double-checked-locking/src/main/java/com/iluwatar/doublecheckedlocking/App.java deleted file mode 100644 index 863f20882..000000000 --- a/double-checked-locking/src/main/java/com/iluwatar/doublecheckedlocking/App.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.iluwatar.doublecheckedlocking; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -/** - * - * In Inventory we store the items with a given size. However, we do not store - * more items than the inventory size. To address concurrent access problems we - * use double checked locking to add item to inventory. In this method, the - * thread which gets the lock first adds the item. - */ -public class App { - - public static void main(String[] args) { - final Inventory inventory = new Inventory(1000); - ExecutorService executorService = Executors.newFixedThreadPool(3); - for (int i = 0; i < 3; i++) { - executorService.execute(new Runnable() { - @Override - public void run() { - while (inventory.addItem(new Item())) - ; - } - }); - } - } -} diff --git a/double-checked-locking/src/main/java/com/iluwatar/doublecheckedlocking/Inventory.java b/double-checked-locking/src/main/java/com/iluwatar/doublecheckedlocking/Inventory.java deleted file mode 100644 index 368be2f9a..000000000 --- a/double-checked-locking/src/main/java/com/iluwatar/doublecheckedlocking/Inventory.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.iluwatar.doublecheckedlocking; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -public class Inventory { - - private int inventorySize; - private List items; - private Lock lock = new ReentrantLock(); - - public Inventory(int inventorySize) { - this.inventorySize = inventorySize; - this.items = new ArrayList(inventorySize); - } - - public boolean addItem(Item item) { - if (items.size() < inventorySize) { - lock.lock(); - try { - if (items.size() < inventorySize) { - items.add(item); - System.out.println(Thread.currentThread()); - return true; - } - } finally { - lock.unlock(); - } - } - return false; - } - -} diff --git a/double-checked-locking/src/main/java/com/iluwatar/doublecheckedlocking/Item.java b/double-checked-locking/src/main/java/com/iluwatar/doublecheckedlocking/Item.java deleted file mode 100644 index bca41c51e..000000000 --- a/double-checked-locking/src/main/java/com/iluwatar/doublecheckedlocking/Item.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.iluwatar.doublecheckedlocking; - -public class Item { - String name; - int level; -} diff --git a/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/AppTest.java b/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/AppTest.java new file mode 100644 index 000000000..748c66c6a --- /dev/null +++ b/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/AppTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.doublechecked.locking; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/InventoryTest.java b/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/InventoryTest.java new file mode 100644 index 000000000..485c9573e --- /dev/null +++ b/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/InventoryTest.java @@ -0,0 +1,132 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.doublechecked.locking; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import java.io.PrintStream; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import static junit.framework.Assert.assertTrue; +import static junit.framework.TestCase.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/10/15 - 9:34 PM + * + * @author Jeroen Meulemeester + */ +public class InventoryTest { + + /** + * The mocked standard out {@link PrintStream}, used to verify a steady increasing size of the + * {@link Inventory} while adding items from multiple threads concurrently + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * The number of threads used to stress test the locking of the {@link Inventory#addItem(Item)} + * method + */ + private static final int THREAD_COUNT = 8; + + /** + * The maximum number of {@link Item}s allowed in the {@link Inventory} + */ + private static final int INVENTORY_SIZE = 1000; + + /** + * Concurrently add multiple items to the inventory, and check if the items were added in order by + * checking the stdOut for continuous growth of the inventory. When 'items.size()=xx' shows up out + * of order, it means that the locking is not ok, increasing the risk of going over the inventory + * item limit. + */ + @Test(timeout = 10000) + public void testAddItem() throws Exception { + // Create a new inventory with a limit of 1000 items and put some load on the add method + final Inventory inventory = new Inventory(INVENTORY_SIZE); + final ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT); + for (int i = 0; i < THREAD_COUNT; i++) { + executorService.execute(() -> { + while (inventory.addItem(new Item())) {}; + }); + } + + // Wait until all threads have finished + executorService.shutdown(); + executorService.awaitTermination(5, TimeUnit.SECONDS); + + // Check the number of items in the inventory. It should not have exceeded the allowed maximum + final List items = inventory.getItems(); + assertNotNull(items); + assertEquals(INVENTORY_SIZE, items.size()); + + // Capture all stdOut messages ... + final ArgumentCaptor stdOutCaptor = ArgumentCaptor.forClass(String.class); + verify(this.stdOutMock, times(INVENTORY_SIZE)).println(stdOutCaptor.capture()); + + // ... verify if we got all 1000 + final List values = stdOutCaptor.getAllValues(); + assertEquals(INVENTORY_SIZE, values.size()); + + // ... and check if the inventory size is increasing continuously + for (int i = 0; i < values.size(); i++) { + assertNotNull(values.get(i)); + assertTrue(values.get(i).contains("items.size()=" + (i + 1))); + } + + verifyNoMoreInteractions(this.stdOutMock); + } + +} \ No newline at end of file diff --git a/double-checked-locking/src/test/java/com/iluwatar/doublecheckedlocking/AppTest.java b/double-checked-locking/src/test/java/com/iluwatar/doublecheckedlocking/AppTest.java deleted file mode 100644 index ca3d82871..000000000 --- a/double-checked-locking/src/test/java/com/iluwatar/doublecheckedlocking/AppTest.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.iluwatar.doublecheckedlocking; - -import org.junit.Test; - -import com.iluwatar.doublecheckedlocking.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} diff --git a/double-dispatch/README.md b/double-dispatch/README.md new file mode 100644 index 000000000..ae87208a2 --- /dev/null +++ b/double-dispatch/README.md @@ -0,0 +1,26 @@ +--- +layout: pattern +title: Double Dispatch +folder: double-dispatch +permalink: /patterns/double-dispatch/ +categories: Other +tags: + - Java + - Difficulty-Intermediate + - Idiom +--- + +## Intent +Double Dispatch pattern is a way to create maintainable dynamic +behavior based on receiver and parameter types. + +![alt text](./etc/double-dispatch.png "Double Dispatch") + +## Applicability +Use the Double Dispatch pattern when + +* the dynamic behavior is not defined only based on receiving object's type but also on the receiving method's parameter type. + +## Real world examples + +* [ObjectOutputStream](https://docs.oracle.com/javase/8/docs/api/java/io/ObjectOutputStream.html) diff --git a/double-dispatch/pom.xml b/double-dispatch/pom.xml index 40356e018..4f31b2e7e 100644 --- a/double-dispatch/pom.xml +++ b/double-dispatch/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT double-dispatch @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/App.java b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/App.java index fa799ecfc..40a0485a5 100644 --- a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/App.java +++ b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.doubledispatch; import java.util.ArrayList; @@ -5,42 +27,50 @@ import java.util.List; /** * - * When a message with a parameter is sent to an object, the resultant behaviour is defined by the - * implementation of that method in the receiver. Sometimes the behaviour must also be determined - * by the type of the parameter. - * - * One way to implement this would be to create multiple instanceof-checks for the methods parameter. - * However, this creates a maintenance issue. When new types are added we would also need to change - * the method's implementation and add a new instanceof-check. This violates the single responsibility - * principle - a class should have only one reason to change. - * - * Instead of the instanceof-checks a better way is to make another virtual call on the parameter - * object. This way new functionality can be easily added without the need to modify existing - * implementation (open-closed principle). - * - * In this example we have hierarchy of objects (GameObject) that can collide to each other. Each - * object has its own coordinates which are checked against the other objects' coordinates. If - * there is an overlap, then the objects collide utilizing the Double Dispatch pattern. + * When a message with a parameter is sent to an object, the resultant behaviour is defined by the implementation of + * that method in the receiver. Sometimes the behaviour must also be determined by the type of the parameter. + *

+ * One way to implement this would be to create multiple instanceof-checks for the methods parameter. However, this + * creates a maintenance issue. When new types are added we would also need to change the method's implementation and + * add a new instanceof-check. This violates the single responsibility principle - a class should have only one reason + * to change. + *

+ * Instead of the instanceof-checks a better way is to make another virtual call on the parameter object. This way new + * functionality can be easily added without the need to modify existing implementation (open-closed principle). + *

+ * In this example we have hierarchy of objects ({@link GameObject}) that can collide to each other. Each object has its + * own coordinates which are checked against the other objects' coordinates. If there is an overlap, then the objects + * collide utilizing the Double Dispatch pattern. * */ public class App { - - public static void main( String[] args ) { - // initialize game objects and print their status - List objects = new ArrayList<>(); - objects.add(new FlamingAsteroid(0, 0, 5, 5)); - objects.add(new SpaceStationMir(1, 1, 2, 2)); - objects.add(new Meteoroid(10, 10, 15, 15)); - objects.add(new SpaceStationIss(12, 12, 14, 14)); - objects.stream().forEach(o -> System.out.println(o)); - System.out.println(""); - - // collision check - objects.stream().forEach(o1 -> objects.stream().forEach(o2 -> { if (o1 != o2 && o1.intersectsWith(o2)) o1.collision(o2); } )); - System.out.println(""); - - // output eventual object statuses - objects.stream().forEach(o -> System.out.println(o)); - System.out.println(""); - } + + /** + * Program entry point + * + * @param args + * command line args + */ + public static void main(String[] args) { + // initialize game objects and print their status + List objects = new ArrayList<>(); + objects.add(new FlamingAsteroid(0, 0, 5, 5)); + objects.add(new SpaceStationMir(1, 1, 2, 2)); + objects.add(new Meteoroid(10, 10, 15, 15)); + objects.add(new SpaceStationIss(12, 12, 14, 14)); + objects.stream().forEach(o -> System.out.println(o)); + System.out.println(""); + + // collision check + objects.stream().forEach(o1 -> objects.stream().forEach(o2 -> { + if (o1 != o2 && o1.intersectsWith(o2)) { + o1.collision(o2); + } + })); + System.out.println(""); + + // output eventual object statuses + objects.stream().forEach(o -> System.out.println(o)); + System.out.println(""); + } } diff --git a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/FlamingAsteroid.java b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/FlamingAsteroid.java index 73b633e5b..6cff89f58 100644 --- a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/FlamingAsteroid.java +++ b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/FlamingAsteroid.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.doubledispatch; /** @@ -7,13 +29,13 @@ package com.iluwatar.doubledispatch; */ public class FlamingAsteroid extends Meteoroid { - public FlamingAsteroid(int left, int top, int right, int bottom) { - super(left, top, right, bottom); - setOnFire(true); - } + public FlamingAsteroid(int left, int top, int right, int bottom) { + super(left, top, right, bottom); + setOnFire(true); + } - @Override - public void collision(GameObject gameObject) { - gameObject.collisionResolve(this); - } + @Override + public void collision(GameObject gameObject) { + gameObject.collisionResolve(this); + } } diff --git a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/GameObject.java b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/GameObject.java index afe05e9b2..fea0cdfd1 100644 --- a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/GameObject.java +++ b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/GameObject.java @@ -1,49 +1,70 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.doubledispatch; /** * - * Game objects have coordinates and some - * other status information. + * Game objects have coordinates and some other status information. * */ public abstract class GameObject extends Rectangle { - - private boolean damaged; - private boolean onFire; - - public GameObject(int left, int top, int right, int bottom) { - super(left, top, right, bottom); - } - - @Override - public String toString() { - return String.format("%s at %s damaged=%b onFire=%b", this.getClass().getSimpleName(), - super.toString(), isDamaged(), isOnFire()); - } - - public boolean isOnFire() { - return onFire; - } - - public void setOnFire(boolean onFire) { - this.onFire = onFire; - } - - public boolean isDamaged() { - return damaged; - } - - public void setDamaged(boolean damaged) { - this.damaged = damaged; - } - - public abstract void collision(GameObject gameObject); - - public abstract void collisionResolve(FlamingAsteroid asteroid); - public abstract void collisionResolve(Meteoroid meteoroid); + private boolean damaged; + private boolean onFire; - public abstract void collisionResolve(SpaceStationMir mir); + public GameObject(int left, int top, int right, int bottom) { + super(left, top, right, bottom); + } - public abstract void collisionResolve(SpaceStationIss iss); + @Override + public String toString() { + return String.format("%s at %s damaged=%b onFire=%b", this.getClass().getSimpleName(), + super.toString(), isDamaged(), isOnFire()); + } + + public boolean isOnFire() { + return onFire; + } + + public void setOnFire(boolean onFire) { + this.onFire = onFire; + } + + public boolean isDamaged() { + return damaged; + } + + public void setDamaged(boolean damaged) { + this.damaged = damaged; + } + + public abstract void collision(GameObject gameObject); + + public abstract void collisionResolve(FlamingAsteroid asteroid); + + public abstract void collisionResolve(Meteoroid meteoroid); + + public abstract void collisionResolve(SpaceStationMir mir); + + public abstract void collisionResolve(SpaceStationIss iss); } diff --git a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/Meteoroid.java b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/Meteoroid.java index fb59c49b4..cc68a85ec 100644 --- a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/Meteoroid.java +++ b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/Meteoroid.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.doubledispatch; /** @@ -7,32 +29,36 @@ package com.iluwatar.doubledispatch; */ public class Meteoroid extends GameObject { - public Meteoroid(int left, int top, int right, int bottom) { - super(left, top, right, bottom); - } + public Meteoroid(int left, int top, int right, int bottom) { + super(left, top, right, bottom); + } - @Override - public void collision(GameObject gameObject) { - gameObject.collisionResolve(this); - } + @Override + public void collision(GameObject gameObject) { + gameObject.collisionResolve(this); + } - @Override - public void collisionResolve(FlamingAsteroid asteroid) { - System.out.println(String.format("%s hits %s.", asteroid.getClass().getSimpleName(), this.getClass().getSimpleName())); - } + @Override + public void collisionResolve(FlamingAsteroid asteroid) { + System.out.println(String.format("%s hits %s.", asteroid.getClass().getSimpleName(), this + .getClass().getSimpleName())); + } - @Override - public void collisionResolve(Meteoroid meteoroid) { - System.out.println(String.format("%s hits %s.", meteoroid.getClass().getSimpleName(), this.getClass().getSimpleName())); - } + @Override + public void collisionResolve(Meteoroid meteoroid) { + System.out.println(String.format("%s hits %s.", meteoroid.getClass().getSimpleName(), this + .getClass().getSimpleName())); + } - @Override - public void collisionResolve(SpaceStationMir mir) { - System.out.println(String.format("%s hits %s.", mir.getClass().getSimpleName(), this.getClass().getSimpleName())); - } + @Override + public void collisionResolve(SpaceStationMir mir) { + System.out.println(String.format("%s hits %s.", mir.getClass().getSimpleName(), this.getClass() + .getSimpleName())); + } - @Override - public void collisionResolve(SpaceStationIss iss) { - System.out.println(String.format("%s hits %s.", iss.getClass().getSimpleName(), this.getClass().getSimpleName())); - } + @Override + public void collisionResolve(SpaceStationIss iss) { + System.out.println(String.format("%s hits %s.", iss.getClass().getSimpleName(), this.getClass() + .getSimpleName())); + } } diff --git a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/Rectangle.java b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/Rectangle.java index 12aca0056..496bb8769 100644 --- a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/Rectangle.java +++ b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/Rectangle.java @@ -1,44 +1,72 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.doubledispatch; /** * - * Rectangle has coordinates and can be checked for overlap against - * other Rectangles. + * Rectangle has coordinates and can be checked for overlap against other Rectangles. * */ public class Rectangle { - private int left; - private int top; - private int right; - private int bottom; + private int left; + private int top; + private int right; + private int bottom; - public Rectangle(int left, int top, int right, int bottom) { - this.left = left; - this.top = top; - this.right = right; - this.bottom = bottom; - } - - public int getLeft() { - return left; - } - public int getTop() { - return top; - } - public int getRight() { - return right; - } - public int getBottom() { - return bottom; - } - - boolean intersectsWith(Rectangle r) { - return !(r.getLeft() > getRight() || r.getRight() < getLeft() || r.getTop() > getBottom() || r.getBottom() < getTop()); - } - - @Override - public String toString() { - return String.format("[%d,%d,%d,%d]", getLeft(), getTop(), getRight(), getBottom()); - } + /** + * Constructor + */ + public Rectangle(int left, int top, int right, int bottom) { + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + + public int getLeft() { + return left; + } + + public int getTop() { + return top; + } + + public int getRight() { + return right; + } + + public int getBottom() { + return bottom; + } + + boolean intersectsWith(Rectangle r) { + return !(r.getLeft() > getRight() || r.getRight() < getLeft() || r.getTop() > getBottom() || r + .getBottom() < getTop()); + } + + @Override + public String toString() { + return String.format("[%d,%d,%d,%d]", getLeft(), getTop(), getRight(), getBottom()); + } } diff --git a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/SpaceStationIss.java b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/SpaceStationIss.java index 50eae3df8..1150fc60b 100644 --- a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/SpaceStationIss.java +++ b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/SpaceStationIss.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.doubledispatch; /** @@ -7,12 +29,12 @@ package com.iluwatar.doubledispatch; */ public class SpaceStationIss extends SpaceStationMir { - public SpaceStationIss(int left, int top, int right, int bottom) { - super(left, top, right, bottom); - } + public SpaceStationIss(int left, int top, int right, int bottom) { + super(left, top, right, bottom); + } - @Override - public void collision(GameObject gameObject) { - gameObject.collisionResolve(this); - } + @Override + public void collision(GameObject gameObject) { + gameObject.collisionResolve(this); + } } diff --git a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/SpaceStationMir.java b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/SpaceStationMir.java index 059b41271..e7a55d0ee 100644 --- a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/SpaceStationMir.java +++ b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/SpaceStationMir.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.doubledispatch; /** @@ -7,45 +29,42 @@ package com.iluwatar.doubledispatch; */ public class SpaceStationMir extends GameObject { - public SpaceStationMir(int left, int top, int right, int bottom) { - super(left, top, right, bottom); - } + public SpaceStationMir(int left, int top, int right, int bottom) { + super(left, top, right, bottom); + } - @Override - public void collision(GameObject gameObject) { - gameObject.collisionResolve(this); - } + @Override + public void collision(GameObject gameObject) { + gameObject.collisionResolve(this); + } - @Override - public void collisionResolve(FlamingAsteroid asteroid) { - System.out.println(String.format("%s hits %s. %s is damaged! %s is set on fire!", - asteroid.getClass().getSimpleName(), this.getClass().getSimpleName(), - this.getClass().getSimpleName(), this.getClass().getSimpleName())); - setDamaged(true); - setOnFire(true); - } + @Override + public void collisionResolve(FlamingAsteroid asteroid) { + System.out.println(String.format("%s hits %s. %s is damaged! %s is set on fire!", asteroid + .getClass().getSimpleName(), this.getClass().getSimpleName(), this.getClass() + .getSimpleName(), this.getClass().getSimpleName())); + setDamaged(true); + setOnFire(true); + } - @Override - public void collisionResolve(Meteoroid meteoroid) { - System.out.println(String.format("%s hits %s. %s is damaged!", - meteoroid.getClass().getSimpleName(), this.getClass().getSimpleName(), - this.getClass().getSimpleName())); - setDamaged(true); - } + @Override + public void collisionResolve(Meteoroid meteoroid) { + System.out.println(String.format("%s hits %s. %s is damaged!", meteoroid.getClass() + .getSimpleName(), this.getClass().getSimpleName(), this.getClass().getSimpleName())); + setDamaged(true); + } - @Override - public void collisionResolve(SpaceStationMir mir) { - System.out.println(String.format("%s hits %s. %s is damaged!", - mir.getClass().getSimpleName(), this.getClass().getSimpleName(), - this.getClass().getSimpleName())); - setDamaged(true); - } + @Override + public void collisionResolve(SpaceStationMir mir) { + System.out.println(String.format("%s hits %s. %s is damaged!", mir.getClass().getSimpleName(), + this.getClass().getSimpleName(), this.getClass().getSimpleName())); + setDamaged(true); + } - @Override - public void collisionResolve(SpaceStationIss iss) { - System.out.println(String.format("%s hits %s. %s is damaged!", - iss.getClass().getSimpleName(), this.getClass().getSimpleName(), - this.getClass().getSimpleName())); - setDamaged(true); - } + @Override + public void collisionResolve(SpaceStationIss iss) { + System.out.println(String.format("%s hits %s. %s is damaged!", iss.getClass().getSimpleName(), + this.getClass().getSimpleName(), this.getClass().getSimpleName())); + setDamaged(true); + } } diff --git a/double-dispatch/src/test/java/com/iluwatar/doubledispatch/AppTest.java b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/AppTest.java index 892772e09..c1a6aa690 100644 --- a/double-dispatch/src/test/java/com/iluwatar/doubledispatch/AppTest.java +++ b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/AppTest.java @@ -1,14 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.doubledispatch; import org.junit.Test; -import com.iluwatar.doubledispatch.App; - +/** + * + * Application test + * + */ public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } + + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/double-dispatch/src/test/java/com/iluwatar/doubledispatch/CollisionTest.java b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/CollisionTest.java new file mode 100644 index 000000000..dbc8fc55e --- /dev/null +++ b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/CollisionTest.java @@ -0,0 +1,158 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.doubledispatch; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; +import java.util.Objects; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class CollisionTest { + + /** + * The mocked standard out {@link PrintStream}, required if some of the actions on the tested + * object don't have a direct influence on any other accessible objects, except for writing to + * std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + + /** + * Get the tested object + * + * @return The tested object, should never return 'null' + */ + abstract O getTestedObject(); + + /** + * Collide the tested item with the other given item and verify if the damage and fire state is as + * expected + * + * @param other The other object we have to collide with + * @param otherDamaged Indicates if the other object should be damaged after the collision + * @param otherOnFire Indicates if the other object should be burning after the collision + * @param thisDamaged Indicates if the test object should be damaged after the collision + * @param thisOnFire Indicates if the other object should be burning after the collision + * @param description The expected description of the collision + */ + void testCollision(final GameObject other, final boolean otherDamaged, final boolean otherOnFire, + final boolean thisDamaged, final boolean thisOnFire, final String description) { + + Objects.requireNonNull(other); + Objects.requireNonNull(getTestedObject()); + + final O tested = getTestedObject(); + + tested.collision(other); + + verify(getStdOutMock(), times(1)).println(description); + verifyNoMoreInteractions(getStdOutMock()); + + testOnFire(other, tested, otherOnFire); + testDamaged(other, tested, otherDamaged); + + testOnFire(tested, other, thisOnFire); + testDamaged(tested, other, thisDamaged); + + } + + /** + * Test if the fire state of the target matches the expected state after colliding with the given + * object + * + * @param target The target object + * @param other The other object + * @param expectTargetOnFire The expected state of fire on the target object + */ + private void testOnFire(final GameObject target, final GameObject other, final boolean expectTargetOnFire) { + final String targetName = target.getClass().getSimpleName(); + final String otherName = other.getClass().getSimpleName(); + + final String errorMessage = expectTargetOnFire + ? "Expected [" + targetName + "] to be on fire after colliding with [" + otherName + "] but it was not!" + : "Expected [" + targetName + "] not to be on fire after colliding with [" + otherName + "] but it was!"; + + assertEquals(errorMessage, expectTargetOnFire, target.isOnFire()); + } + + /** + * Test if the damage state of the target matches the expected state after colliding with the + * given object + * + * @param target The target object + * @param other The other object + * @param expectedDamage The expected state of damage on the target object + */ + private void testDamaged(final GameObject target, final GameObject other, final boolean expectedDamage) { + final String targetName = target.getClass().getSimpleName(); + final String otherName = other.getClass().getSimpleName(); + + final String errorMessage = expectedDamage + ? "Expected [" + targetName + "] to be damaged after colliding with [" + otherName + "] but it was not!" + : "Expected [" + targetName + "] not to be damaged after colliding with [" + otherName + "] but it was!"; + + assertEquals(errorMessage, expectedDamage, target.isDamaged()); + } + +} diff --git a/double-dispatch/src/test/java/com/iluwatar/doubledispatch/FlamingAsteroidTest.java b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/FlamingAsteroidTest.java new file mode 100644 index 000000000..31a16f093 --- /dev/null +++ b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/FlamingAsteroidTest.java @@ -0,0 +1,110 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.doubledispatch; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Date: 12/10/15 - 11:31 PM + * + * @author Jeroen Meulemeester + */ +public class FlamingAsteroidTest extends CollisionTest { + + @Override + final FlamingAsteroid getTestedObject() { + return new FlamingAsteroid(1, 2, 3, 4); + } + + /** + * Test the constructor parameters + */ + @Test + public void testConstructor() { + final FlamingAsteroid asteroid = new FlamingAsteroid(1, 2, 3, 4); + assertEquals(1, asteroid.getLeft()); + assertEquals(2, asteroid.getTop()); + assertEquals(3, asteroid.getRight()); + assertEquals(4, asteroid.getBottom()); + assertTrue(asteroid.isOnFire()); + assertFalse(asteroid.isDamaged()); + assertEquals("FlamingAsteroid at [1,2,3,4] damaged=false onFire=true", asteroid.toString()); + } + + /** + * Test what happens we collide with an asteroid + */ + @Test + public void testCollideFlamingAsteroid() { + testCollision( + new FlamingAsteroid(1, 2, 3, 4), + false, true, + false, true, + "FlamingAsteroid hits FlamingAsteroid." + ); + } + + /** + * Test what happens we collide with an meteoroid + */ + @Test + public void testCollideMeteoroid() { + testCollision( + new Meteoroid(1, 1, 3, 4), + false, false, + false, true, + "FlamingAsteroid hits Meteoroid." + ); + } + + /** + * Test what happens we collide with ISS + */ + @Test + public void testCollideSpaceStationIss() { + testCollision( + new SpaceStationIss(1, 1, 3, 4), + true, true, + false, true, + "FlamingAsteroid hits SpaceStationIss. SpaceStationIss is damaged! SpaceStationIss is set on fire!" + ); + } + + /** + * Test what happens we collide with MIR + */ + @Test + public void testCollideSpaceStationMir() { + testCollision( + new SpaceStationMir(1, 1, 3, 4), + true, true, + false, true, + "FlamingAsteroid hits SpaceStationMir. SpaceStationMir is damaged! SpaceStationMir is set on fire!" + ); + } + +} \ No newline at end of file diff --git a/double-dispatch/src/test/java/com/iluwatar/doubledispatch/MeteoroidTest.java b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/MeteoroidTest.java new file mode 100644 index 000000000..147fe430a --- /dev/null +++ b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/MeteoroidTest.java @@ -0,0 +1,109 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.doubledispatch; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +/** + * Date: 12/10/15 - 11:31 PM + * + * @author Jeroen Meulemeester + */ +public class MeteoroidTest extends CollisionTest { + + @Override + final Meteoroid getTestedObject() { + return new Meteoroid(1, 2, 3, 4); + } + + /** + * Test the constructor parameters + */ + @Test + public void testConstructor() { + final Meteoroid meteoroid = new Meteoroid(1, 2, 3, 4); + assertEquals(1, meteoroid.getLeft()); + assertEquals(2, meteoroid.getTop()); + assertEquals(3, meteoroid.getRight()); + assertEquals(4, meteoroid.getBottom()); + assertFalse(meteoroid.isOnFire()); + assertFalse(meteoroid.isDamaged()); + assertEquals("Meteoroid at [1,2,3,4] damaged=false onFire=false", meteoroid.toString()); + } + + /** + * Test what happens we collide with an asteroid + */ + @Test + public void testCollideFlamingAsteroid() { + testCollision( + new FlamingAsteroid(1, 1, 3, 4), + false, true, + false, false, + "Meteoroid hits FlamingAsteroid." + ); + } + + /** + * Test what happens we collide with an meteoroid + */ + @Test + public void testCollideMeteoroid() { + testCollision( + new Meteoroid(1, 1, 3, 4), + false, false, + false, false, + "Meteoroid hits Meteoroid." + ); + } + + /** + * Test what happens we collide with ISS + */ + @Test + public void testCollideSpaceStationIss() { + testCollision( + new SpaceStationIss(1, 1, 3, 4), + true, false, + false, false, + "Meteoroid hits SpaceStationIss. SpaceStationIss is damaged!" + ); + } + + /** + * Test what happens we collide with MIR + */ + @Test + public void testCollideSpaceStationMir() { + testCollision( + new SpaceStationMir(1, 1, 3, 4), + true, false, + false, false, + "Meteoroid hits SpaceStationMir. SpaceStationMir is damaged!" + ); + } + +} \ No newline at end of file diff --git a/double-dispatch/src/test/java/com/iluwatar/doubledispatch/RectangleTest.java b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/RectangleTest.java index bb8aa954e..b7c0dbd2e 100644 --- a/double-dispatch/src/test/java/com/iluwatar/doubledispatch/RectangleTest.java +++ b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/RectangleTest.java @@ -1,22 +1,69 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.doubledispatch; -import org.junit.Assert; import org.junit.Test; -import com.iluwatar.doubledispatch.Rectangle; +import static junit.framework.TestCase.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; /** - * * Unit test for Rectangle - * */ public class RectangleTest { - @Test - public void test() { - Assert.assertTrue(new Rectangle(0,0,1,1).intersectsWith(new Rectangle(0,0,1,1))); - Assert.assertTrue(new Rectangle(0,0,1,1).intersectsWith(new Rectangle(-1,-5,7,8))); - Assert.assertFalse(new Rectangle(0,0,1,1).intersectsWith(new Rectangle(2,2,3,3))); - Assert.assertFalse(new Rectangle(0,0,1,1).intersectsWith(new Rectangle(-2,-2,-1,-1))); - } + /** + * Test if the values passed through the constructor matches the values fetched from the getters + */ + @Test + public void testConstructor() { + final Rectangle rectangle = new Rectangle(1, 2, 3, 4); + assertEquals(1, rectangle.getLeft()); + assertEquals(2, rectangle.getTop()); + assertEquals(3, rectangle.getRight()); + assertEquals(4, rectangle.getBottom()); + } + + /** + * Test if the values passed through the constructor matches the values in the {@link + * #toString()} + */ + @Test + public void testToString() throws Exception { + final Rectangle rectangle = new Rectangle(1, 2, 3, 4); + assertEquals("[1,2,3,4]", rectangle.toString()); + } + + /** + * Test if the {@link Rectangle} class can detect if it intersects with another rectangle. + */ + @Test + public void testIntersection() { + assertTrue(new Rectangle(0, 0, 1, 1).intersectsWith(new Rectangle(0, 0, 1, 1))); + assertTrue(new Rectangle(0, 0, 1, 1).intersectsWith(new Rectangle(-1, -5, 7, 8))); + assertFalse(new Rectangle(0, 0, 1, 1).intersectsWith(new Rectangle(2, 2, 3, 3))); + assertFalse(new Rectangle(0, 0, 1, 1).intersectsWith(new Rectangle(-2, -2, -1, -1))); + } + } diff --git a/double-dispatch/src/test/java/com/iluwatar/doubledispatch/SpaceStationIssTest.java b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/SpaceStationIssTest.java new file mode 100644 index 000000000..d06f84b22 --- /dev/null +++ b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/SpaceStationIssTest.java @@ -0,0 +1,109 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.doubledispatch; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +/** + * Date: 12/10/15 - 11:31 PM + * + * @author Jeroen Meulemeester + */ +public class SpaceStationIssTest extends CollisionTest { + + @Override + final SpaceStationIss getTestedObject() { + return new SpaceStationIss(1, 2, 3, 4); + } + + /** + * Test the constructor parameters + */ + @Test + public void testConstructor() { + final SpaceStationIss iss = new SpaceStationIss(1, 2, 3, 4); + assertEquals(1, iss.getLeft()); + assertEquals(2, iss.getTop()); + assertEquals(3, iss.getRight()); + assertEquals(4, iss.getBottom()); + assertFalse(iss.isOnFire()); + assertFalse(iss.isDamaged()); + assertEquals("SpaceStationIss at [1,2,3,4] damaged=false onFire=false", iss.toString()); + } + + /** + * Test what happens we collide with an asteroid + */ + @Test + public void testCollideFlamingAsteroid() { + testCollision( + new FlamingAsteroid(1, 1, 3, 4), + false, true, + false, false, + "SpaceStationIss hits FlamingAsteroid." + ); + } + + /** + * Test what happens we collide with an meteoroid + */ + @Test + public void testCollideMeteoroid() { + testCollision( + new Meteoroid(1, 1, 3, 4), + false, false, + false, false, + "SpaceStationIss hits Meteoroid." + ); + } + + /** + * Test what happens we collide with ISS + */ + @Test + public void testCollideSpaceStationIss() { + testCollision( + new SpaceStationIss(1, 1, 3, 4), + true, false, + false, false, + "SpaceStationIss hits SpaceStationIss. SpaceStationIss is damaged!" + ); + } + + /** + * Test what happens we collide with MIR + */ + @Test + public void testCollideSpaceStationMir() { + testCollision( + new SpaceStationMir(1, 1, 3, 4), + true, false, + false, false, + "SpaceStationIss hits SpaceStationMir. SpaceStationMir is damaged!" + ); + } + +} \ No newline at end of file diff --git a/double-dispatch/src/test/java/com/iluwatar/doubledispatch/SpaceStationMirTest.java b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/SpaceStationMirTest.java new file mode 100644 index 000000000..c107aed8b --- /dev/null +++ b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/SpaceStationMirTest.java @@ -0,0 +1,109 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.doubledispatch; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +/** + * Date: 12/10/15 - 11:31 PM + * + * @author Jeroen Meulemeester + */ +public class SpaceStationMirTest extends CollisionTest { + + @Override + final SpaceStationMir getTestedObject() { + return new SpaceStationMir(1, 2, 3, 4); + } + + /** + * Test the constructor parameters + */ + @Test + public void testConstructor() { + final SpaceStationMir mir = new SpaceStationMir(1, 2, 3, 4); + assertEquals(1, mir.getLeft()); + assertEquals(2, mir.getTop()); + assertEquals(3, mir.getRight()); + assertEquals(4, mir.getBottom()); + assertFalse(mir.isOnFire()); + assertFalse(mir.isDamaged()); + assertEquals("SpaceStationMir at [1,2,3,4] damaged=false onFire=false", mir.toString()); + } + + /** + * Test what happens we collide with an asteroid + */ + @Test + public void testCollideFlamingAsteroid() { + testCollision( + new FlamingAsteroid(1, 1, 3, 4), + false, true, + false, false, + "SpaceStationMir hits FlamingAsteroid." + ); + } + + /** + * Test what happens we collide with an meteoroid + */ + @Test + public void testCollideMeteoroid() { + testCollision( + new Meteoroid(1, 1, 3, 4), + false, false, + false, false, + "SpaceStationMir hits Meteoroid." + ); + } + + /** + * Test what happens we collide with ISS + */ + @Test + public void testCollideSpaceStationIss() { + testCollision( + new SpaceStationIss(1, 1, 3, 4), + true, false, + false, false, + "SpaceStationMir hits SpaceStationIss. SpaceStationIss is damaged!" + ); + } + + /** + * Test what happens we collide with MIR + */ + @Test + public void testCollideSpaceStationMir() { + testCollision( + new SpaceStationMir(1, 1, 3, 4), + true, false, + false, false, + "SpaceStationMir hits SpaceStationMir. SpaceStationMir is damaged!" + ); + } + +} \ No newline at end of file diff --git a/event-aggregator/README.md b/event-aggregator/README.md new file mode 100644 index 000000000..ac07869e7 --- /dev/null +++ b/event-aggregator/README.md @@ -0,0 +1,34 @@ +--- +layout: pattern +title: Event Aggregator +folder: event-aggregator +permalink: /patterns/event-aggregator/ +categories: Structural +tags: + - Java + - Difficulty-Beginner + - Reactive +--- + +## Intent +A system with lots of objects can lead to complexities when a +client wants to subscribe to events. The client has to find and register for +each object individually, if each object has multiple events then each event +requires a separate subscription. An Event Aggregator acts as a single source +of events for many objects. It registers for all the events of the many objects +allowing clients to register with just the aggregator. + +![alt text](./etc/classes.png "Event Aggregator") + +## Applicability +Use the Event Aggregator pattern when + +* Event Aggregator is a good choice when you have lots of objects that are + potential event sources. Rather than have the observer deal with registering + with them all, you can centralize the registration logic to the Event + Aggregator. As well as simplifying registration, a Event Aggregator also + simplifies the memory management issues in using observers. + +## Credits + +* [Martin Fowler - Event Aggregator](http://martinfowler.com/eaaDev/EventAggregator.html) diff --git a/event-aggregator/pom.xml b/event-aggregator/pom.xml index 910d6a35e..b7de6e01b 100644 --- a/event-aggregator/pom.xml +++ b/event-aggregator/pom.xml @@ -1,10 +1,34 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT event-aggregator @@ -13,5 +37,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/App.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/App.java new file mode 100644 index 000000000..879355b65 --- /dev/null +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/App.java @@ -0,0 +1,66 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.event.aggregator; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * A system with lots of objects can lead to complexities when a client wants to subscribe to + * events. The client has to find and register for each object individually, if each object has + * multiple events then each event requires a separate subscription. + *

+ * An Event Aggregator acts as a single source of events for many objects. It registers for all the + * events of the many objects allowing clients to register with just the aggregator. + *

+ * In the example {@link LordBaelish}, {@link LordVarys} and {@link Scout} deliver events to + * {@link KingsHand}. {@link KingsHand}, the event aggregator, then delivers the events to + * {@link KingJoffrey}. + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + + KingJoffrey kingJoffrey = new KingJoffrey(); + KingsHand kingsHand = new KingsHand(kingJoffrey); + + List emitters = new ArrayList<>(); + emitters.add(kingsHand); + emitters.add(new LordBaelish(kingsHand)); + emitters.add(new LordVarys(kingsHand)); + emitters.add(new Scout(kingsHand)); + + for (Weekday day : Weekday.values()) { + for (EventEmitter emitter : emitters) { + emitter.timePasses(day); + } + } + } +} diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Event.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Event.java new file mode 100644 index 000000000..7397530ef --- /dev/null +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Event.java @@ -0,0 +1,44 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.event.aggregator; + +/** + * + * Event enumeration. + * + */ +public enum Event { + + STARK_SIGHTED("Stark sighted"), WARSHIPS_APPROACHING("Warships approaching"), TRAITOR_DETECTED( + "Traitor detected"); + + private String description; + + Event(String description) { + this.description = description; + } + + public String toString() { + return description; + } +} diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/EventEmitter.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/EventEmitter.java new file mode 100644 index 000000000..b34235df5 --- /dev/null +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/EventEmitter.java @@ -0,0 +1,57 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.event.aggregator; + +import java.util.LinkedList; +import java.util.List; + +/** + * + * EventEmitter is the base class for event producers that can be observed. + * + */ +public abstract class EventEmitter { + + private List observers; + + public EventEmitter() { + observers = new LinkedList<>(); + } + + public EventEmitter(EventObserver obs) { + this(); + registerObserver(obs); + } + + public final void registerObserver(EventObserver obs) { + observers.add(obs); + } + + protected void notifyObservers(Event e) { + for (EventObserver obs : observers) { + obs.onEvent(e); + } + } + + public abstract void timePasses(Weekday day); +} diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/EventObserver.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/EventObserver.java new file mode 100644 index 000000000..020f23284 --- /dev/null +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/EventObserver.java @@ -0,0 +1,34 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.event.aggregator; + +/** + * + * Observers of events implement this interface. + * + */ +public interface EventObserver { + + void onEvent(Event e); + +} diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/KingJoffrey.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/KingJoffrey.java new file mode 100644 index 000000000..fdda59693 --- /dev/null +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/KingJoffrey.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.event.aggregator; + +/** + * + * KingJoffrey observes events from {@link KingsHand}. + * + */ +public class KingJoffrey implements EventObserver { + + @Override + public void onEvent(Event e) { + System.out.println("Received event from the King's Hand: " + e.toString()); + } +} diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/KingsHand.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/KingsHand.java new file mode 100644 index 000000000..32c8d98d6 --- /dev/null +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/KingsHand.java @@ -0,0 +1,49 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.event.aggregator; + +/** + * + * KingsHand observes events from multiple sources and delivers them to listeners. + * + */ +public class KingsHand extends EventEmitter implements EventObserver { + + public KingsHand() { + super(); + } + + public KingsHand(EventObserver obs) { + super(obs); + } + + @Override + public void onEvent(Event e) { + notifyObservers(e); + } + + @Override + public void timePasses(Weekday day) { + // NOP + } +} diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/LordBaelish.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/LordBaelish.java new file mode 100644 index 000000000..2fdfeada9 --- /dev/null +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/LordBaelish.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.event.aggregator; + +/** + * + * LordBaelish produces events. + * + */ +public class LordBaelish extends EventEmitter { + + public LordBaelish() { + super(); + } + + public LordBaelish(EventObserver obs) { + super(obs); + } + + @Override + public void timePasses(Weekday day) { + if (day.equals(Weekday.FRIDAY)) { + notifyObservers(Event.STARK_SIGHTED); + } + } +} diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/LordVarys.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/LordVarys.java new file mode 100644 index 000000000..b22708d63 --- /dev/null +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/LordVarys.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.event.aggregator; + +/** + * + * LordVarys produces events. + * + */ +public class LordVarys extends EventEmitter { + + public LordVarys() { + super(); + } + + public LordVarys(EventObserver obs) { + super(obs); + } + + @Override + public void timePasses(Weekday day) { + if (day.equals(Weekday.SATURDAY)) { + notifyObservers(Event.TRAITOR_DETECTED); + } + } +} diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Scout.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Scout.java new file mode 100644 index 000000000..3b0945367 --- /dev/null +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Scout.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.event.aggregator; + +/** + * + * Scout produces events. + * + */ +public class Scout extends EventEmitter { + + public Scout() { + super(); + } + + public Scout(EventObserver obs) { + super(obs); + } + + @Override + public void timePasses(Weekday day) { + if (day.equals(Weekday.TUESDAY)) { + notifyObservers(Event.WARSHIPS_APPROACHING); + } + } +} diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Weekday.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Weekday.java new file mode 100644 index 000000000..d6f10ce22 --- /dev/null +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Weekday.java @@ -0,0 +1,44 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.event.aggregator; + +/** + * + * Weekday enumeration + * + */ +public enum Weekday { + + MONDAY("Monday"), TUESDAY("Tuesday"), WEDNESDAY("Wednesday"), THURSDAY("Thursday"), FRIDAY( + "Friday"), SATURDAY("Saturday"), SUNDAY("Sunday"); + + private String description; + + Weekday(String description) { + this.description = description; + } + + public String toString() { + return description; + } +} diff --git a/event-aggregator/src/main/java/com/iluwatar/eventaggregator/App.java b/event-aggregator/src/main/java/com/iluwatar/eventaggregator/App.java deleted file mode 100644 index 1b13dfdaa..000000000 --- a/event-aggregator/src/main/java/com/iluwatar/eventaggregator/App.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.iluwatar.eventaggregator; - -import java.util.ArrayList; -import java.util.List; - -/** - * - * The Event Aggregator pattern channels events from multiple objects - * into a single object to simplify registration for clients. - * - * In the example LordBaelish, LordVarys and Scout deliver events to - * KingsHand. KingsHand, the event aggregator, then delivers the events - * to KingJoffrey. - * - */ -public class App { - - public static void main(String[] args) { - - KingJoffrey kingJoffrey = new KingJoffrey(); - KingsHand kingsHand = new KingsHand(kingJoffrey); - - List emitters = new ArrayList<>(); - emitters.add(kingsHand); - emitters.add(new LordBaelish(kingsHand)); - emitters.add(new LordVarys(kingsHand)); - emitters.add(new Scout(kingsHand)); - - for (Weekday day: Weekday.values()) { - for (EventEmitter emitter: emitters) { - emitter.timePasses(day); - } - } - } -} diff --git a/event-aggregator/src/main/java/com/iluwatar/eventaggregator/Event.java b/event-aggregator/src/main/java/com/iluwatar/eventaggregator/Event.java deleted file mode 100644 index fbceeb01d..000000000 --- a/event-aggregator/src/main/java/com/iluwatar/eventaggregator/Event.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.iluwatar.eventaggregator; - -/** - * - * Event enumeration. - * - */ -public enum Event { - - STARK_SIGHTED("Stark sighted"), WARSHIPS_APPROACHING("Warships approaching"), TRAITOR_DETECTED("Traitor detected"); - - private String description; - - Event(String description) { - this.description = description; - } - - public String toString() { - return description; - } -} diff --git a/event-aggregator/src/main/java/com/iluwatar/eventaggregator/EventEmitter.java b/event-aggregator/src/main/java/com/iluwatar/eventaggregator/EventEmitter.java deleted file mode 100644 index 88c23178f..000000000 --- a/event-aggregator/src/main/java/com/iluwatar/eventaggregator/EventEmitter.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.iluwatar.eventaggregator; - -import java.util.LinkedList; -import java.util.List; - -/** - * - * EventEmitter is the base class for event producers that can be observed. - * - */ -public abstract class EventEmitter { - - private List observers; - - public EventEmitter() { - observers = new LinkedList<>(); - } - - public EventEmitter(EventObserver obs) { - this(); - registerObserver(obs); - } - - public void registerObserver(EventObserver obs) { - observers.add(obs); - } - - protected void notifyObservers(Event e) { - for (EventObserver obs: observers) { - obs.onEvent(e); - } - } - - public abstract void timePasses(Weekday day); -} diff --git a/event-aggregator/src/main/java/com/iluwatar/eventaggregator/EventObserver.java b/event-aggregator/src/main/java/com/iluwatar/eventaggregator/EventObserver.java deleted file mode 100644 index c3dc54a47..000000000 --- a/event-aggregator/src/main/java/com/iluwatar/eventaggregator/EventObserver.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.iluwatar.eventaggregator; - -/** - * - * Observers of events implement this interface. - * - */ -public interface EventObserver { - - void onEvent(Event e); - -} diff --git a/event-aggregator/src/main/java/com/iluwatar/eventaggregator/KingJoffrey.java b/event-aggregator/src/main/java/com/iluwatar/eventaggregator/KingJoffrey.java deleted file mode 100644 index 642dfc787..000000000 --- a/event-aggregator/src/main/java/com/iluwatar/eventaggregator/KingJoffrey.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.iluwatar.eventaggregator; - -/** - * - * KingJoffrey observes events from KingsHand. - * - */ -public class KingJoffrey implements EventObserver { - - @Override - public void onEvent(Event e) { - System.out.println("Received event from the King's Hand: " + e.toString()); - } -} diff --git a/event-aggregator/src/main/java/com/iluwatar/eventaggregator/KingsHand.java b/event-aggregator/src/main/java/com/iluwatar/eventaggregator/KingsHand.java deleted file mode 100644 index 2b8e1e769..000000000 --- a/event-aggregator/src/main/java/com/iluwatar/eventaggregator/KingsHand.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.iluwatar.eventaggregator; - -/** - * - * KingsHand observes events from multiple sources and delivers them - * to listeners. - * - */ -public class KingsHand extends EventEmitter implements EventObserver { - - public KingsHand() { - super(); - } - - public KingsHand(EventObserver obs) { - super(obs); - } - - @Override - public void onEvent(Event e) { - notifyObservers(e); - } - - @Override - public void timePasses(Weekday day) { - // NOP - } -} diff --git a/event-aggregator/src/main/java/com/iluwatar/eventaggregator/LordBaelish.java b/event-aggregator/src/main/java/com/iluwatar/eventaggregator/LordBaelish.java deleted file mode 100644 index e3f0223a1..000000000 --- a/event-aggregator/src/main/java/com/iluwatar/eventaggregator/LordBaelish.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.iluwatar.eventaggregator; - -/** - * - * LordBaelish produces events. - * - */ -public class LordBaelish extends EventEmitter { - - public LordBaelish() { - super(); - } - - public LordBaelish(EventObserver obs) { - super(obs); - } - - @Override - public void timePasses(Weekday day) { - if (day.equals(Weekday.FRIDAY)) { - notifyObservers(Event.STARK_SIGHTED); - } - } -} diff --git a/event-aggregator/src/main/java/com/iluwatar/eventaggregator/LordVarys.java b/event-aggregator/src/main/java/com/iluwatar/eventaggregator/LordVarys.java deleted file mode 100644 index 0afaaf150..000000000 --- a/event-aggregator/src/main/java/com/iluwatar/eventaggregator/LordVarys.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.iluwatar.eventaggregator; - -/** - * - * LordVarys produces events. - * - */ -public class LordVarys extends EventEmitter { - - public LordVarys() { - super(); - } - - public LordVarys(EventObserver obs) { - super(obs); - } - - @Override - public void timePasses(Weekday day) { - if (day.equals(Weekday.SATURDAY)) { - notifyObservers(Event.TRAITOR_DETECTED); - } - } -} diff --git a/event-aggregator/src/main/java/com/iluwatar/eventaggregator/Scout.java b/event-aggregator/src/main/java/com/iluwatar/eventaggregator/Scout.java deleted file mode 100644 index 6376182b7..000000000 --- a/event-aggregator/src/main/java/com/iluwatar/eventaggregator/Scout.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.iluwatar.eventaggregator; - -/** - * - * Scout produces events. - * - */ -public class Scout extends EventEmitter { - - public Scout() { - super(); - } - - public Scout(EventObserver obs) { - super(obs); - } - - @Override - public void timePasses(Weekday day) { - if (day.equals(Weekday.TUESDAY)) { - notifyObservers(Event.WARSHIPS_APPROACHING); - } - } -} diff --git a/event-aggregator/src/main/java/com/iluwatar/eventaggregator/Weekday.java b/event-aggregator/src/main/java/com/iluwatar/eventaggregator/Weekday.java deleted file mode 100644 index b62cbb106..000000000 --- a/event-aggregator/src/main/java/com/iluwatar/eventaggregator/Weekday.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.iluwatar.eventaggregator; - -public enum Weekday { - - MONDAY("Monday"), TUESDAY("Tuesday"), WEDNESDAY("Wednesday"), THURSDAY("Thursday"), FRIDAY("Friday"), SATURDAY("Saturday"), SUNDAY("Sunday"); - - private String description; - - Weekday(String description) { - this.description = description; - } - - public String toString() { - return description; - } -} diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/AppTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/AppTest.java new file mode 100644 index 000000000..2330e1f1e --- /dev/null +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/AppTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.event.aggregator; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/EventEmitterTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/EventEmitterTest.java new file mode 100644 index 000000000..63fc31a1f --- /dev/null +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/EventEmitterTest.java @@ -0,0 +1,155 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.event.aggregator; + +import org.junit.Test; + +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Supplier; + +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/12/15 - 10:58 PM + * + * @author Jeroen Meulemeester + */ +public abstract class EventEmitterTest { + + /** + * Factory used to create a new instance of the test object with a default observer + */ + private final Function factoryWithDefaultObserver; + + /** + * Factory used to create a new instance of the test object without passing a default observer + */ + private final Supplier factoryWithoutDefaultObserver; + + /** + * The day of the week an event is expected + */ + private final Weekday specialDay; + + /** + * The expected event, emitted on the special day + */ + private final Event event; + + /** + * Create a new event emitter test, using the given test object factories, special day and event + */ + EventEmitterTest(final Weekday specialDay, final Event event, + final Function factoryWithDefaultObserver, + final Supplier factoryWithoutDefaultObserver) { + + this.specialDay = specialDay; + this.event = event; + this.factoryWithDefaultObserver = Objects.requireNonNull(factoryWithDefaultObserver); + this.factoryWithoutDefaultObserver = Objects.requireNonNull(factoryWithoutDefaultObserver); + } + + /** + * Go over every day of the month, and check if the event is emitted on the given day. This test + * is executed twice, once without a default emitter and once with + */ + @Test + public void testAllDays() { + testAllDaysWithoutDefaultObserver(specialDay, event); + testAllDaysWithDefaultObserver(specialDay, event); + } + + /** + * Pass each week of the day, day by day to the event emitter and verify of the given observers + * received the correct event on the special day. + * + * @param specialDay The special day on which an event is emitted + * @param event The expected event emitted by the test object + * @param emitter The event emitter + * @param observers The registered observer mocks + */ + private void testAllDays(final Weekday specialDay, final Event event, final E emitter, + final EventObserver... observers) { + + for (final Weekday weekday : Weekday.values()) { + // Pass each week of the day, day by day to the event emitter + emitter.timePasses(weekday); + + if (weekday == specialDay) { + // On a special day, every observer should have received the event + for (final EventObserver observer : observers) { + verify(observer, times(1)).onEvent(eq(event)); + } + } else { + // On any other normal day, the observers should have received nothing at all + verifyZeroInteractions(observers); + } + } + + // The observers should not have received any additional events after the week + verifyNoMoreInteractions(observers); + } + + /** + * Go over every day of the month, and check if the event is emitted on the given day. Use an + * event emitter without a default observer + * + * @param specialDay The special day on which an event is emitted + * @param event The expected event emitted by the test object + */ + private void testAllDaysWithoutDefaultObserver(final Weekday specialDay, final Event event) { + final EventObserver observer1 = mock(EventObserver.class); + final EventObserver observer2 = mock(EventObserver.class); + + final E emitter = this.factoryWithoutDefaultObserver.get(); + emitter.registerObserver(observer1); + emitter.registerObserver(observer2); + + testAllDays(specialDay, event, emitter, observer1, observer2); + } + + /** + * Go over every day of the month, and check if the event is emitted on the given day. + * + * @param specialDay The special day on which an event is emitted + * @param event The expected event emitted by the test object + */ + private void testAllDaysWithDefaultObserver(final Weekday specialDay, final Event event) { + final EventObserver defaultObserver = mock(EventObserver.class); + final EventObserver observer1 = mock(EventObserver.class); + final EventObserver observer2 = mock(EventObserver.class); + + final E emitter = this.factoryWithDefaultObserver.apply(defaultObserver); + emitter.registerObserver(observer1); + emitter.registerObserver(observer2); + + testAllDays(specialDay, event, emitter, defaultObserver, observer1, observer2); + } + +} diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/EventTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/EventTest.java new file mode 100644 index 000000000..33d1796e9 --- /dev/null +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/EventTest.java @@ -0,0 +1,49 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.event.aggregator; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/12/15 - 2:52 PM + * + * @author Jeroen Meulemeester + */ +public class EventTest { + + /** + * Verify if every event has a non-null, non-empty description + */ + @Test + public void testToString() { + for (final Event event : Event.values()) { + final String toString = event.toString(); + assertNotNull(toString); + assertFalse(toString.trim().isEmpty()); + } + } + +} \ No newline at end of file diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingJoffreyTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingJoffreyTest.java new file mode 100644 index 000000000..3e0028ac4 --- /dev/null +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingJoffreyTest.java @@ -0,0 +1,89 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.event.aggregator; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/12/15 - 3:04 PM + * + * @author Jeroen Meulemeester + */ +public class KingJoffreyTest { + + /** + * The mocked standard out {@link PrintStream}, required since {@link KingJoffrey} does nothing + * except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Test if {@link KingJoffrey} tells us what event he received + */ + @Test + public void testOnEvent() { + final KingJoffrey kingJoffrey = new KingJoffrey(); + + for (final Event event : Event.values()) { + verifyZeroInteractions(this.stdOutMock); + kingJoffrey.onEvent(event); + + final String expectedMessage = "Received event from the King's Hand: " + event.toString(); + verify(this.stdOutMock, times(1)).println(expectedMessage); + verifyNoMoreInteractions(this.stdOutMock); + } + + } + +} \ No newline at end of file diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingsHandTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingsHandTest.java new file mode 100644 index 000000000..93116a071 --- /dev/null +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingsHandTest.java @@ -0,0 +1,70 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.event.aggregator; + +import org.junit.Test; + +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/12/15 - 10:57 AM + * + * @author Jeroen Meulemeester + */ +public class KingsHandTest extends EventEmitterTest { + + /** + * Create a new test instance, using the correct object factory + */ + public KingsHandTest() { + super(null, null, KingsHand::new, KingsHand::new); + } + + /** + * The {@link KingsHand} is both an {@link EventEmitter} as an {@link EventObserver} so verify if every + * event received is passed up to it's superior, in most cases {@link KingJoffrey} but now just a + * mocked observer. + */ + @Test + public void testPassThrough() throws Exception { + final EventObserver observer = mock(EventObserver.class); + final KingsHand kingsHand = new KingsHand(observer); + + // The kings hand should not pass any events before he received one + verifyZeroInteractions(observer); + + // Verify if each event is passed on to the observer, nothing less, nothing more. + for (final Event event : Event.values()) { + kingsHand.onEvent(event); + verify(observer, times(1)).onEvent(eq(event)); + verifyNoMoreInteractions(observer); + } + + } + +} \ No newline at end of file diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/LordBaelishTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/LordBaelishTest.java new file mode 100644 index 000000000..2432e7b40 --- /dev/null +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/LordBaelishTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.event.aggregator; + +/** + * Date: 12/12/15 - 10:57 AM + * + * @author Jeroen Meulemeester + */ +public class LordBaelishTest extends EventEmitterTest { + + /** + * Create a new test instance, using the correct object factory + */ + public LordBaelishTest() { + super(Weekday.FRIDAY, Event.STARK_SIGHTED, LordBaelish::new, LordBaelish::new); + } + +} \ No newline at end of file diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/LordVarysTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/LordVarysTest.java new file mode 100644 index 000000000..d65c3f8e6 --- /dev/null +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/LordVarysTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.event.aggregator; + +/** + * Date: 12/12/15 - 10:57 AM + * + * @author Jeroen Meulemeester + */ +public class LordVarysTest extends EventEmitterTest { + + /** + * Create a new test instance, using the correct object factory + */ + public LordVarysTest() { + super(Weekday.SATURDAY, Event.TRAITOR_DETECTED, LordVarys::new, LordVarys::new); + } + +} \ No newline at end of file diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/ScoutTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/ScoutTest.java new file mode 100644 index 000000000..701323485 --- /dev/null +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/ScoutTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.event.aggregator; + +/** + * Date: 12/12/15 - 10:57 AM + * + * @author Jeroen Meulemeester + */ +public class ScoutTest extends EventEmitterTest { + + /** + * Create a new test instance, using the correct object factory + */ + public ScoutTest() { + super(Weekday.TUESDAY, Event.WARSHIPS_APPROACHING, Scout::new, Scout::new); + } + +} \ No newline at end of file diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/WeekdayTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/WeekdayTest.java new file mode 100644 index 000000000..1e91aab74 --- /dev/null +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/WeekdayTest.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.event.aggregator; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/12/15 - 2:12 PM + * + * @author Jeroen Meulemeester + */ +public class WeekdayTest { + + @Test + public void testToString() throws Exception { + for (final Weekday weekday : Weekday.values()) { + final String toString = weekday.toString(); + assertNotNull(toString); + assertEquals(weekday.name(), toString.toUpperCase()); + } + } + +} \ No newline at end of file diff --git a/event-aggregator/src/test/java/com/iluwatar/eventaggregator/AppTest.java b/event-aggregator/src/test/java/com/iluwatar/eventaggregator/AppTest.java deleted file mode 100644 index 42710f378..000000000 --- a/event-aggregator/src/test/java/com/iluwatar/eventaggregator/AppTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.iluwatar.eventaggregator; -import org.junit.Test; - -import com.iluwatar.eventaggregator.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} diff --git a/event-driven-architecture/README.md b/event-driven-architecture/README.md new file mode 100644 index 000000000..843e4c268 --- /dev/null +++ b/event-driven-architecture/README.md @@ -0,0 +1,37 @@ +--- +layout: pattern +title: Event Driven Architecture +folder: event-driven-architecture +permalink: /patterns/event-driven-architecture/ +categories: Architectural +tags: + - Java + - Difficulty-Intermediate + - Reactive +--- + +## Intent +Send and notify state changes of your objects to other applications using an Event-driven Architecture. + +![alt text](./etc/eda.png "Event Driven Architecture") + +## Applicability +Use an Event-driven architecture when + +* you want to create a loosely coupled system +* you want to build a more responsive system +* you want a system that is easier to extend + +## Real world examples + +* SendGrid, an email API, sends events whenever an email is processed, delivered, opened etc... (https://sendgrid.com/docs/API_Reference/Webhooks/event.html) +* Chargify, a billing API, exposes payment activity through various events (https://docs.chargify.com/api-events) +* Amazon's AWS Lambda, lets you execute code in response to events such as changes to Amazon S3 buckets, updates to an Amazon DynamoDB table, or custom events generated by your applications or devices. (https://aws.amazon.com/lambda) +* MySQL runs triggers based on events such as inserts and update events happening on database tables. + +## Credits + +* [Event-driven architecture - Wikipedia](http://www.computerweekly.com/feature/Write-through-write-around-write-back-Cache-explained) +* [Fundamental Components of an Event-Driven Architecture](http://giocc.com/fundamental-components-of-an-event-driven-architecture.html) +* [Real World Applications/Event Driven Applications](https://wiki.haskell.org/Real_World_Applications/Event_Driven_Applications) +* [Event-driven architecture definition](http://searchsoa.techtarget.com/definition/event-driven-architecture) diff --git a/event-driven-architecture/etc/eda.png b/event-driven-architecture/etc/eda.png new file mode 100644 index 000000000..743726451 Binary files /dev/null and b/event-driven-architecture/etc/eda.png differ diff --git a/event-driven-architecture/etc/eda.ucls b/event-driven-architecture/etc/eda.ucls new file mode 100644 index 000000000..776bedc81 --- /dev/null +++ b/event-driven-architecture/etc/eda.ucls @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/event-driven-architecture/pom.xml b/event-driven-architecture/pom.xml new file mode 100644 index 000000000..99ed39891 --- /dev/null +++ b/event-driven-architecture/pom.xml @@ -0,0 +1,52 @@ + + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + + event-driven-architecture + + + + junit + junit + test + + + + org.mockito + mockito-core + test + + + \ No newline at end of file diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/App.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/App.java new file mode 100644 index 000000000..866b3c9e9 --- /dev/null +++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/App.java @@ -0,0 +1,64 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.eda; + +import com.iluwatar.eda.event.UserCreatedEvent; +import com.iluwatar.eda.event.UserUpdatedEvent; +import com.iluwatar.eda.framework.Event; +import com.iluwatar.eda.framework.EventDispatcher; +import com.iluwatar.eda.handler.UserCreatedEventHandler; +import com.iluwatar.eda.handler.UserUpdatedEventHandler; +import com.iluwatar.eda.model.User; + +/** + * An event-driven architecture (EDA) is a framework that orchestrates behavior around the + * production, detection and consumption of events as well as the responses they evoke. An event is + * any identifiable occurrence that has significance for system hardware or software.

The + * example below uses an {@link EventDispatcher} to link/register {@link Event} objects to their + * respective handlers once an {@link Event} is dispatched, it's respective handler is invoked and + * the {@link Event} is handled accordingly. + * + */ +public class App { + + /** + * Once the {@link EventDispatcher} is initialised, handlers related to specific events have to be + * made known to the dispatcher by registering them. In this case the {@link UserCreatedEvent} is + * bound to the UserCreatedEventHandler, whilst the {@link UserUpdatedEvent} is bound to the + * {@link UserUpdatedEventHandler}. The dispatcher can now be called to dispatch specific events. + * When a user is saved, the {@link UserCreatedEvent} can be dispatched. + * On the other hand, when a user is updated, {@link UserUpdatedEvent} can be dispatched. + * + */ + public static void main(String[] args) { + + EventDispatcher dispatcher = new EventDispatcher(); + dispatcher.registerHandler(UserCreatedEvent.class, new UserCreatedEventHandler()); + dispatcher.registerHandler(UserUpdatedEvent.class, new UserUpdatedEventHandler()); + + User user = new User("iluwatar"); + dispatcher.dispatch(new UserCreatedEvent(user)); + dispatcher.dispatch(new UserUpdatedEvent(user)); + } + +} diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/event/AbstractEvent.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/event/AbstractEvent.java new file mode 100644 index 000000000..54a916c7b --- /dev/null +++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/event/AbstractEvent.java @@ -0,0 +1,49 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.eda.event; + +import com.iluwatar.eda.framework.EventDispatcher; +import com.iluwatar.eda.framework.Event; + +/** + * The {@link AbstractEvent} class serves as a base class for defining custom events happening with your + * system. In this example we have two types of events defined. + *

    + *
  • {@link UserCreatedEvent} - used when a user is created
  • + *
  • {@link UserUpdatedEvent} - used when a user is updated
  • + *
+ * Events can be distinguished using the {@link #getType() getType} method. + */ +public abstract class AbstractEvent implements Event { + + /** + * Returns the event type as a {@link Class} object + * In this example, this method is used by the {@link EventDispatcher} to + * dispatch events depending on their type. + * + * @return the AbstractEvent type as a {@link Class}. + */ + public Class getType() { + return getClass(); + } +} \ No newline at end of file diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/event/UserCreatedEvent.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/event/UserCreatedEvent.java new file mode 100644 index 000000000..717ed1a9d --- /dev/null +++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/event/UserCreatedEvent.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.eda.event; + +import com.iluwatar.eda.model.User; + +/** + * The {@link UserCreatedEvent} should should be dispatched whenever a user has been created. + * This class can be extended to contain details about the user has been created. In this example, + * the entire {@link User} object is passed on as data with the event. + */ +public class UserCreatedEvent extends AbstractEvent { + + private User user; + + public UserCreatedEvent(User user) { + this.user = user; + } + + public User getUser() { + return user; + } +} diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/event/UserUpdatedEvent.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/event/UserUpdatedEvent.java new file mode 100644 index 000000000..9646957dc --- /dev/null +++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/event/UserUpdatedEvent.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.eda.event; + +import com.iluwatar.eda.model.User; + +/** + * The {@link UserUpdatedEvent} should should be dispatched whenever a user has been updated. + * This class can be extended to contain details about the user has been updated. In this example, + * the entire {@link User} object is passed on as data with the event. + */ +public class UserUpdatedEvent extends AbstractEvent { + + private User user; + + public UserUpdatedEvent(User user) { + this.user = user; + } + + public User getUser() { + return user; + } +} diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/Event.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/Event.java new file mode 100644 index 000000000..c63d2746f --- /dev/null +++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/Event.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.eda.framework; + +/** + * A {@link Event} is an object with a specific type that is associated + * to a specific {@link Handler}. + */ +public interface Event { + + /** + * Returns the message type as a {@link Class} object. In this example the message type is + * used to handle events by their type. + * @return the message type as a {@link Class}. + */ + Class getType(); +} diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/EventDispatcher.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/EventDispatcher.java new file mode 100644 index 000000000..9f8e29315 --- /dev/null +++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/EventDispatcher.java @@ -0,0 +1,64 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.eda.framework; + +import java.util.HashMap; +import java.util.Map; + +/** + * Handles the routing of {@link Event} messages to associated handlers. + * A {@link HashMap} is used to store the association between events and their respective handlers. + */ +public class EventDispatcher { + + private Map, Handler> handlers; + + public EventDispatcher() { + handlers = new HashMap<>(); + } + + /** + * Links an {@link Event} to a specific {@link Handler}. + * + * @param eventType The {@link Event} to be registered + * @param handler The {@link Handler} that will be handling the {@link Event} + */ + public void registerHandler(Class eventType, + Handler handler) { + handlers.put(eventType, handler); + } + + /** + * Dispatches an {@link Event} depending on it's type. + * + * @param event The {@link Event} to be dispatched + */ + @SuppressWarnings("unchecked") + public void dispatch(E event) { + Handler handler = (Handler) handlers.get(event.getClass()); + if (handler != null) { + handler.onEvent(event); + } + } + +} \ No newline at end of file diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/Handler.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/Handler.java new file mode 100644 index 000000000..44bdab6dc --- /dev/null +++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/Handler.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.eda.framework; + +/** + * This interface can be implemented to handle different types of messages. + * Every handler is responsible for a single of type message + */ +public interface Handler { + + /** + * The onEvent method should implement and handle behavior related to the event. + * This can be as simple as calling another service to handle the event on publishing the event on + * a queue to be consumed by other sub systems. + * @param event the {@link Event} object to be handled. + */ + void onEvent(E event); +} \ No newline at end of file diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/handler/UserCreatedEventHandler.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/handler/UserCreatedEventHandler.java new file mode 100644 index 000000000..3ef4e8255 --- /dev/null +++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/handler/UserCreatedEventHandler.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.eda.handler; + +import com.iluwatar.eda.event.UserCreatedEvent; +import com.iluwatar.eda.framework.Handler; + +/** + * Handles the {@link UserCreatedEvent} message. + */ +public class UserCreatedEventHandler implements Handler { + + @Override + public void onEvent(UserCreatedEvent event) { + + System.out.println(String.format( + "User '%s' has been Created!", event.getUser().getUsername())); + } + +} diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/handler/UserUpdatedEventHandler.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/handler/UserUpdatedEventHandler.java new file mode 100644 index 000000000..0311d5781 --- /dev/null +++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/handler/UserUpdatedEventHandler.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.eda.handler; + +import com.iluwatar.eda.event.UserUpdatedEvent; +import com.iluwatar.eda.framework.Handler; + +/** + * Handles the {@link UserUpdatedEvent} message. + */ +public class UserUpdatedEventHandler implements Handler { + + @Override + public void onEvent(UserUpdatedEvent event) { + + System.out.println(String.format( + "User '%s' has been Updated!", event.getUser().getUsername())); + } +} diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/model/User.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/model/User.java new file mode 100644 index 000000000..82ef960de --- /dev/null +++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/model/User.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.eda.model; + +import com.iluwatar.eda.event.UserCreatedEvent; +import com.iluwatar.eda.event.UserUpdatedEvent; + +/** + * This {@link User} class is a basic pojo used to demonstrate user data sent along with + * the {@link UserCreatedEvent} and {@link UserUpdatedEvent} events. + */ +public class User { + + private String username; + + public User(String username) { + this.username = username; + } + + public String getUsername() { + return username; + } +} diff --git a/event-driven-architecture/src/test/java/com/iluwatar/eda/AppTest.java b/event-driven-architecture/src/test/java/com/iluwatar/eda/AppTest.java new file mode 100644 index 000000000..603d0a61b --- /dev/null +++ b/event-driven-architecture/src/test/java/com/iluwatar/eda/AppTest.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.eda; + +import org.junit.Test; + +import java.io.IOException; + +/** + * Tests that Event Driven Architecture example runs without errors. + */ +public class AppTest { + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } +} diff --git a/event-driven-architecture/src/test/java/com/iluwatar/eda/event/UserCreatedEventTest.java b/event-driven-architecture/src/test/java/com/iluwatar/eda/event/UserCreatedEventTest.java new file mode 100644 index 000000000..b9074faf2 --- /dev/null +++ b/event-driven-architecture/src/test/java/com/iluwatar/eda/event/UserCreatedEventTest.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.eda.event; + +import com.iluwatar.eda.model.User; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * {@link UserCreatedEventTest} tests and verifies {@link AbstractEvent} behaviour. + */ +public class UserCreatedEventTest { + + /** + * This unit test should correctly return the {@link AbstractEvent} class type when calling the + * {@link AbstractEvent#getType() getType} method. + */ + @Test + public void testGetEventType() { + User user = new User("iluwatar"); + UserCreatedEvent userCreatedEvent = new UserCreatedEvent(user); + assertEquals(UserCreatedEvent.class, userCreatedEvent.getType()); + } +} diff --git a/event-driven-architecture/src/test/java/com/iluwatar/eda/framework/EventDispatcherTest.java b/event-driven-architecture/src/test/java/com/iluwatar/eda/framework/EventDispatcherTest.java new file mode 100644 index 000000000..21956afec --- /dev/null +++ b/event-driven-architecture/src/test/java/com/iluwatar/eda/framework/EventDispatcherTest.java @@ -0,0 +1,70 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.eda.framework; + +import com.iluwatar.eda.event.UserCreatedEvent; +import com.iluwatar.eda.event.UserUpdatedEvent; +import com.iluwatar.eda.handler.UserCreatedEventHandler; +import com.iluwatar.eda.handler.UserUpdatedEventHandler; +import com.iluwatar.eda.model.User; + +import org.junit.Test; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +/** + * Event Dispatcher unit tests to assert and verify correct event dispatcher behaviour + */ +public class EventDispatcherTest { + + /** + * This unit test should register events and event handlers correctly with the event dispatcher + * and events should be dispatched accordingly. + */ + @Test + public void testEventDriverPattern() { + + EventDispatcher dispatcher = spy(new EventDispatcher()); + UserCreatedEventHandler userCreatedEventHandler = spy(new UserCreatedEventHandler()); + UserUpdatedEventHandler userUpdatedEventHandler = spy(new UserUpdatedEventHandler()); + dispatcher.registerHandler(UserCreatedEvent.class, userCreatedEventHandler); + dispatcher.registerHandler(UserUpdatedEvent.class, userUpdatedEventHandler); + + User user = new User("iluwatar"); + + UserCreatedEvent userCreatedEvent = new UserCreatedEvent(user); + UserUpdatedEvent userUpdatedEvent = new UserUpdatedEvent(user); + + //fire a userCreatedEvent and verify that userCreatedEventHandler has been invoked. + dispatcher.dispatch(userCreatedEvent); + verify(userCreatedEventHandler).onEvent(userCreatedEvent); + verify(dispatcher).dispatch(userCreatedEvent); + + //fire a userCreatedEvent and verify that userUpdatedEventHandler has been invoked. + dispatcher.dispatch(userUpdatedEvent); + verify(userUpdatedEventHandler).onEvent(userUpdatedEvent); + verify(dispatcher).dispatch(userUpdatedEvent); + } + +} diff --git a/exclude-pmd.properties b/exclude-pmd.properties new file mode 100644 index 000000000..aeda4353d --- /dev/null +++ b/exclude-pmd.properties @@ -0,0 +1,26 @@ +# +# The MIT License +# Copyright (c) 2014 Ilkka Seppälä +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +com.iluwatar.servicelayer.common.BaseEntity=UnusedPrivateField +com.iluwatar.doublechecked.locking.App=EmptyStatementNotInLoop,EmptyWhileStmt +com.iluwatar.doublechecked.locking.InventoryTest=EmptyStatementNotInLoop,EmptyWhileStmt diff --git a/execute-around/README.md b/execute-around/README.md new file mode 100644 index 000000000..f669f18ff --- /dev/null +++ b/execute-around/README.md @@ -0,0 +1,27 @@ +--- +layout: pattern +title: Execute Around +folder: execute-around +permalink: /patterns/execute-around/ +categories: Other +tags: + - Java + - Difficulty-Beginner + - Idiom +--- + +## Intent +Execute Around idiom frees the user from certain actions that +should always be executed before and after the business method. A good example +of this is resource allocation and deallocation leaving the user to specify +only what to do with the resource. + +![alt text](./etc/execute-around.png "Execute Around") + +## Applicability +Use the Execute Around idiom when + +* you use an API that requires methods to be called in pairs such as open/close or allocate/deallocate. + +## Credits +* [Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions](http://www.amazon.com/Functional-Programming-Java-Harnessing-Expressions/dp/1937785467/ref=sr_1_1) diff --git a/execute-around/pom.xml b/execute-around/pom.xml index 1782caf88..60bb6d0ab 100644 --- a/execute-around/pom.xml +++ b/execute-around/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT execute-around diff --git a/execute-around/src/main/java/com/iluwatar/execute/around/App.java b/execute-around/src/main/java/com/iluwatar/execute/around/App.java new file mode 100644 index 000000000..f8ccebdcf --- /dev/null +++ b/execute-around/src/main/java/com/iluwatar/execute/around/App.java @@ -0,0 +1,55 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.execute.around; + +import java.io.FileWriter; +import java.io.IOException; + +/** + * The Execute Around idiom specifies some code to be executed before and after a method. Typically + * the idiom is used when the API has methods to be executed in pairs, such as resource + * allocation/deallocation or lock acquisition/release. + *

+ * In this example, we have {@link SimpleFileWriter} class that opens and closes the file for the + * user. The user specifies only what to do with the file by providing the {@link FileWriterAction} + * implementation. + * + */ +public class App { + + /** + * Program entry point + */ + public static void main(String[] args) throws IOException { + + new SimpleFileWriter("testfile.txt", new FileWriterAction() { + + @Override + public void writeFile(FileWriter writer) throws IOException { + writer.write("Hello"); + writer.append(" "); + writer.append("there!"); + } + }); + } +} diff --git a/execute-around/src/main/java/com/iluwatar/execute/around/FileWriterAction.java b/execute-around/src/main/java/com/iluwatar/execute/around/FileWriterAction.java new file mode 100644 index 000000000..159786134 --- /dev/null +++ b/execute-around/src/main/java/com/iluwatar/execute/around/FileWriterAction.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.execute.around; + +import java.io.FileWriter; +import java.io.IOException; + +/** + * + * Interface for specifying what to do with the file resource. + * + */ +public interface FileWriterAction { + + void writeFile(FileWriter writer) throws IOException; + +} diff --git a/execute-around/src/main/java/com/iluwatar/execute/around/SimpleFileWriter.java b/execute-around/src/main/java/com/iluwatar/execute/around/SimpleFileWriter.java new file mode 100644 index 000000000..111bad73e --- /dev/null +++ b/execute-around/src/main/java/com/iluwatar/execute/around/SimpleFileWriter.java @@ -0,0 +1,47 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.execute.around; + +import java.io.FileWriter; +import java.io.IOException; + +/** + * + * SimpleFileWriter handles opening and closing file for the user. The user only has to specify what + * to do with the file resource through {@link FileWriterAction} parameter. + * + */ +public class SimpleFileWriter { + + /** + * Constructor + */ + public SimpleFileWriter(String filename, FileWriterAction action) throws IOException { + FileWriter writer = new FileWriter(filename); + try { + action.writeFile(writer); + } finally { + writer.close(); + } + } +} diff --git a/execute-around/src/main/java/com/iluwatar/executearound/App.java b/execute-around/src/main/java/com/iluwatar/executearound/App.java deleted file mode 100644 index 783693fac..000000000 --- a/execute-around/src/main/java/com/iluwatar/executearound/App.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.iluwatar.executearound; - -import java.io.FileWriter; -import java.io.IOException; - -/** - * The Execute Around idiom specifies some code to be executed before and after - * a method. Typically the idiom is used when the API has methods to be executed in - * pairs, such as resource allocation/deallocation or lock acquisition/release. - * - * In this example, we have SimpleFileWriter class that opens and closes the file - * for the user. The user specifies only what to do with the file by providing the - * FileWriterAction implementation. - * - */ -public class App { - - public static void main( String[] args ) throws IOException { - - new SimpleFileWriter("testfile.txt", new FileWriterAction() { - - @Override - public void writeFile(FileWriter writer) throws IOException { - writer.write("Hello"); - writer.append(" "); - writer.append("there!"); - } - }); - } -} diff --git a/execute-around/src/main/java/com/iluwatar/executearound/FileWriterAction.java b/execute-around/src/main/java/com/iluwatar/executearound/FileWriterAction.java deleted file mode 100644 index 12eea7f66..000000000 --- a/execute-around/src/main/java/com/iluwatar/executearound/FileWriterAction.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.iluwatar.executearound; - -import java.io.FileWriter; -import java.io.IOException; - -/** - * - * Interface for specifying what to do with the file resource. - * - */ -public interface FileWriterAction { - - void writeFile(FileWriter writer) throws IOException; - -} diff --git a/execute-around/src/main/java/com/iluwatar/executearound/SimpleFileWriter.java b/execute-around/src/main/java/com/iluwatar/executearound/SimpleFileWriter.java deleted file mode 100644 index ee7f23360..000000000 --- a/execute-around/src/main/java/com/iluwatar/executearound/SimpleFileWriter.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.iluwatar.executearound; - -import java.io.FileWriter; -import java.io.IOException; - -/** - * - * SimpleFileWriter handles opening and closing file for the user. The user - * only has to specify what to do with the file resource through FileWriterAction - * parameter. - * - */ -public class SimpleFileWriter { - - public SimpleFileWriter(String filename, FileWriterAction action) throws IOException { - FileWriter writer = new FileWriter(filename); - try { - action.writeFile(writer); - } finally { - writer.close(); - } - } -} diff --git a/execute-around/src/test/java/com/iluwatar/execute/around/AppTest.java b/execute-around/src/test/java/com/iluwatar/execute/around/AppTest.java new file mode 100644 index 000000000..b74f53a25 --- /dev/null +++ b/execute-around/src/test/java/com/iluwatar/execute/around/AppTest.java @@ -0,0 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.execute.around; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; + +/** + * + * Tests execute-around example. + * + */ +public class AppTest { + + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } + + @Before + @After + public void cleanup() { + File file = new File("testfile.txt"); + file.delete(); + } +} diff --git a/execute-around/src/test/java/com/iluwatar/execute/around/SimpleFileWriterTest.java b/execute-around/src/test/java/com/iluwatar/execute/around/SimpleFileWriterTest.java new file mode 100644 index 000000000..abad14935 --- /dev/null +++ b/execute-around/src/test/java/com/iluwatar/execute/around/SimpleFileWriterTest.java @@ -0,0 +1,96 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.execute.around; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Date: 12/12/15 - 3:21 PM + * + * @author Jeroen Meulemeester + */ +public class SimpleFileWriterTest { + + /** + * Create a temporary folder, used to generate files in during this test + */ + @Rule + public final TemporaryFolder testFolder = new TemporaryFolder(); + + /** + * Verify if the given writer is not 'null' + */ + @Test + public void testWriterNotNull() throws Exception { + final File temporaryFile = this.testFolder.newFile(); + new SimpleFileWriter(temporaryFile.getPath(), Assert::assertNotNull); + } + + /** + * Test if the {@link SimpleFileWriter} creates a file if it doesn't exist + */ + @Test + public void testNonExistentFile() throws Exception { + final File nonExistingFile = new File(this.testFolder.getRoot(), "non-existing-file"); + assertFalse(nonExistingFile.exists()); + + new SimpleFileWriter(nonExistingFile.getPath(), Assert::assertNotNull); + assertTrue(nonExistingFile.exists()); + } + + /** + * Test if the data written to the file writer actually gets in the file + */ + @Test + public void testActualWrite() throws Exception { + final String testMessage = "Test message"; + + final File temporaryFile = this.testFolder.newFile(); + assertTrue(temporaryFile.exists()); + + new SimpleFileWriter(temporaryFile.getPath(), writer -> writer.write(testMessage)); + assertTrue(Files.lines(temporaryFile.toPath()).allMatch(testMessage::equals)); + } + + /** + * Verify if an {@link IOException} during the write ripples through + */ + @Test(expected = IOException.class) + public void testIoException() throws Exception { + final File temporaryFile = this.testFolder.newFile(); + new SimpleFileWriter(temporaryFile.getPath(), writer -> { + throw new IOException(""); + }); + } + +} diff --git a/execute-around/src/test/java/com/iluwatar/executearound/AppTest.java b/execute-around/src/test/java/com/iluwatar/executearound/AppTest.java deleted file mode 100644 index 263595cd8..000000000 --- a/execute-around/src/test/java/com/iluwatar/executearound/AppTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.iluwatar.executearound; - -import java.io.File; -import java.io.IOException; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import com.iluwatar.executearound.App; - -/** - * - * Tests execute-around example. - * - */ -public class AppTest { - - @Test - public void test() throws IOException { - String[] args = {}; - App.main(args); - } - - @Before - @After - public void cleanup() { - File file = new File("testfile.txt"); - file.delete(); - } -} diff --git a/facade/README.md b/facade/README.md new file mode 100644 index 000000000..c416552c7 --- /dev/null +++ b/facade/README.md @@ -0,0 +1,28 @@ +--- +layout: pattern +title: Facade +folder: facade +permalink: /patterns/facade/ +categories: Structural +tags: + - Java + - Gang Of Four + - Difficulty-Beginner +--- + +## Intent +Provide a unified interface to a set of interfaces in a subsystem. +Facade defines a higher-level interface that makes the subsystem easier to use. + +![alt text](./etc/facade_1.png "Facade") + +## Applicability +Use the Facade pattern when + +* you want to provide a simple interface to a complex subsystem. Subsystems often get more complex as they evolve. Most patterns, when applied, result in more and smaller classes. This makes the subsystem more reusable and easier to customize, but it also becomes harder to use for clients that don't need to customize it. A facade can provide a simple default view of the subsystem that is good enough for most clients. Only clients needing more customizability will need to look beyond the facade. +* there are many dependencies between clients and the implementation classes of an abstraction. Introduce a facade to decouple the subsystem from clients and other subsystems, thereby promoting subsystem independence and portability. +* you want to layer your subsystems. Use a facade to define an entry point to each subsystem level. If subsystems are dependent, the you can simplify the dependencies between them by making them communicate with each other solely through their facades + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/facade/pom.xml b/facade/pom.xml index b80538341..e0e0f4a4d 100644 --- a/facade/pom.xml +++ b/facade/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT facade @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/facade/src/main/java/com/iluwatar/facade/App.java b/facade/src/main/java/com/iluwatar/facade/App.java index 09d5d1a0b..242bfc9c4 100644 --- a/facade/src/main/java/com/iluwatar/facade/App.java +++ b/facade/src/main/java/com/iluwatar/facade/App.java @@ -1,17 +1,51 @@ -package com.iluwatar.facade; - -/** - * - * Facade (DwarvenGoldmineFacade) provides simpler interface to subsystem. - * http://en.wikipedia.org/wiki/Facade_pattern - * - */ -public class App { - - public static void main(String[] args) { - DwarvenGoldmineFacade facade = new DwarvenGoldmineFacade(); - facade.startNewDay(); - facade.digOutGold(); - facade.endDay(); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.facade; + +/** + * + * The Facade design pattern is often used when a system is very complex or difficult to understand + * because the system has a large number of interdependent classes or its source code is + * unavailable. This pattern hides the complexities of the larger system and provides a simpler + * interface to the client. It typically involves a single wrapper class which contains a set of + * members required by client. These members access the system on behalf of the facade client and + * hide the implementation details. + *

+ * In this example the Facade is ({@link DwarvenGoldmineFacade}) and it provides a simpler interface + * to the goldmine subsystem. + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + DwarvenGoldmineFacade facade = new DwarvenGoldmineFacade(); + facade.startNewDay(); + facade.digOutGold(); + facade.endDay(); + } +} diff --git a/facade/src/main/java/com/iluwatar/facade/DwarvenCartOperator.java b/facade/src/main/java/com/iluwatar/facade/DwarvenCartOperator.java index 7c33fd267..bdc839f57 100644 --- a/facade/src/main/java/com/iluwatar/facade/DwarvenCartOperator.java +++ b/facade/src/main/java/com/iluwatar/facade/DwarvenCartOperator.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.facade; /** @@ -7,14 +29,13 @@ package com.iluwatar.facade; */ public class DwarvenCartOperator extends DwarvenMineWorker { - @Override - public void work() { - System.out.println(name() + " moves gold chunks out of the mine."); - } - - @Override - public String name() { - return "Dwarf cart operator"; - } + @Override + public void work() { + System.out.println(name() + " moves gold chunks out of the mine."); + } + @Override + public String name() { + return "Dwarf cart operator"; + } } diff --git a/facade/src/main/java/com/iluwatar/facade/DwarvenGoldDigger.java b/facade/src/main/java/com/iluwatar/facade/DwarvenGoldDigger.java index b503889b7..54fa821f4 100644 --- a/facade/src/main/java/com/iluwatar/facade/DwarvenGoldDigger.java +++ b/facade/src/main/java/com/iluwatar/facade/DwarvenGoldDigger.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.facade; /** @@ -7,14 +29,13 @@ package com.iluwatar.facade; */ public class DwarvenGoldDigger extends DwarvenMineWorker { - @Override - public void work() { - System.out.println(name() + " digs for gold."); - } - - @Override - public String name() { - return "Dwarf gold digger"; - } + @Override + public void work() { + System.out.println(name() + " digs for gold."); + } + @Override + public String name() { + return "Dwarf gold digger"; + } } diff --git a/facade/src/main/java/com/iluwatar/facade/DwarvenGoldmineFacade.java b/facade/src/main/java/com/iluwatar/facade/DwarvenGoldmineFacade.java index 83a989fa7..4f6e3be4c 100644 --- a/facade/src/main/java/com/iluwatar/facade/DwarvenGoldmineFacade.java +++ b/facade/src/main/java/com/iluwatar/facade/DwarvenGoldmineFacade.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.facade; import java.util.ArrayList; @@ -6,40 +28,42 @@ import java.util.List; /** * - * DwarvenGoldmineFacade provides a single interface - * through which users can operate the subsystems. + * DwarvenGoldmineFacade provides a single interface through which users can operate the subsystems. * - * This makes the goldmine easier to operate and - * cuts the dependencies from the goldmine user to - * the subsystems. + * This makes the goldmine easier to operate and cuts the dependencies from the goldmine user to the + * subsystems. * */ public class DwarvenGoldmineFacade { - private final List workers; + private final List workers; - public DwarvenGoldmineFacade() { - workers = new ArrayList<>(); - workers.add(new DwarvenGoldDigger()); - workers.add(new DwarvenCartOperator()); - workers.add(new DwarvenTunnelDigger()); - } + /** + * Constructor + */ + public DwarvenGoldmineFacade() { + workers = new ArrayList<>(); + workers.add(new DwarvenGoldDigger()); + workers.add(new DwarvenCartOperator()); + workers.add(new DwarvenTunnelDigger()); + } - public void startNewDay() { - makeActions(workers, DwarvenMineWorker.Action.WAKE_UP, DwarvenMineWorker.Action.GO_TO_MINE); - } + public void startNewDay() { + makeActions(workers, DwarvenMineWorker.Action.WAKE_UP, DwarvenMineWorker.Action.GO_TO_MINE); + } - public void digOutGold() { - makeActions(workers, DwarvenMineWorker.Action.WORK); - } + public void digOutGold() { + makeActions(workers, DwarvenMineWorker.Action.WORK); + } - public void endDay() { - makeActions(workers, DwarvenMineWorker.Action.GO_HOME, DwarvenMineWorker.Action.GO_TO_SLEEP); - } + public void endDay() { + makeActions(workers, DwarvenMineWorker.Action.GO_HOME, DwarvenMineWorker.Action.GO_TO_SLEEP); + } - private void makeActions(Collection workers, DwarvenMineWorker.Action... actions) { - for (DwarvenMineWorker worker : workers) { - worker.action(actions); - } + private static void makeActions(Collection workers, + DwarvenMineWorker.Action... actions) { + for (DwarvenMineWorker worker : workers) { + worker.action(actions); } + } } diff --git a/facade/src/main/java/com/iluwatar/facade/DwarvenMineWorker.java b/facade/src/main/java/com/iluwatar/facade/DwarvenMineWorker.java index 3dd51f907..f27054c53 100644 --- a/facade/src/main/java/com/iluwatar/facade/DwarvenMineWorker.java +++ b/facade/src/main/java/com/iluwatar/facade/DwarvenMineWorker.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.facade; /** @@ -7,56 +29,59 @@ package com.iluwatar.facade; */ public abstract class DwarvenMineWorker { - public void goToSleep() { - System.out.println(name() + " goes to sleep."); - } + public void goToSleep() { + System.out.println(name() + " goes to sleep."); + } - public void wakeUp() { - System.out.println(name() + " wakes up."); - } + public void wakeUp() { + System.out.println(name() + " wakes up."); + } - public void goHome() { - System.out.println(name() + " goes home."); - } + public void goHome() { + System.out.println(name() + " goes home."); + } - public void goToMine() { - System.out.println(name() + " goes to the mine."); - } + public void goToMine() { + System.out.println(name() + " goes to the mine."); + } - private void action(Action action) { - switch (action) { - case GO_TO_SLEEP: - goToSleep(); - break; - case WAKE_UP: - wakeUp(); - break; - case GO_HOME: - goHome(); - break; - case GO_TO_MINE: - goToMine(); - break; - case WORK: - work(); - break; - default: - System.out.println("Undefined action"); - break; - } + private void action(Action action) { + switch (action) { + case GO_TO_SLEEP: + goToSleep(); + break; + case WAKE_UP: + wakeUp(); + break; + case GO_HOME: + goHome(); + break; + case GO_TO_MINE: + goToMine(); + break; + case WORK: + work(); + break; + default: + System.out.println("Undefined action"); + break; } + } - public void action(Action... actions) { - for (Action action : actions) { - action(action); - } + /** + * Perform actions + */ + public void action(Action... actions) { + for (Action action : actions) { + action(action); } + } - public abstract void work(); + public abstract void work(); - public abstract String name(); + public abstract String name(); - static enum Action { - GO_TO_SLEEP, WAKE_UP, GO_HOME, GO_TO_MINE, WORK - } + static enum Action { + GO_TO_SLEEP, WAKE_UP, GO_HOME, GO_TO_MINE, WORK + } } diff --git a/facade/src/main/java/com/iluwatar/facade/DwarvenTunnelDigger.java b/facade/src/main/java/com/iluwatar/facade/DwarvenTunnelDigger.java index 05fe02fb6..74d8b89cc 100644 --- a/facade/src/main/java/com/iluwatar/facade/DwarvenTunnelDigger.java +++ b/facade/src/main/java/com/iluwatar/facade/DwarvenTunnelDigger.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.facade; /** @@ -7,14 +29,13 @@ package com.iluwatar.facade; */ public class DwarvenTunnelDigger extends DwarvenMineWorker { - @Override - public void work() { - System.out.println(name() + " creates another promising tunnel."); - } - - @Override - public String name() { - return "Dwarven tunnel digger"; - } + @Override + public void work() { + System.out.println(name() + " creates another promising tunnel."); + } + @Override + public String name() { + return "Dwarven tunnel digger"; + } } diff --git a/facade/src/test/java/com/iluwatar/facade/AppTest.java b/facade/src/test/java/com/iluwatar/facade/AppTest.java index b32b16bb1..115fcc405 100644 --- a/facade/src/test/java/com/iluwatar/facade/AppTest.java +++ b/facade/src/test/java/com/iluwatar/facade/AppTest.java @@ -1,14 +1,39 @@ -package com.iluwatar.facade; - -import org.junit.Test; - -import com.iluwatar.facade.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.facade; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/facade/src/test/java/com/iluwatar/facade/DwarvenGoldmineFacadeTest.java b/facade/src/test/java/com/iluwatar/facade/DwarvenGoldmineFacadeTest.java new file mode 100644 index 000000000..4a3b218e2 --- /dev/null +++ b/facade/src/test/java/com/iluwatar/facade/DwarvenGoldmineFacadeTest.java @@ -0,0 +1,125 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.facade; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.PrintStream; + +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +/** + * Date: 12/9/15 - 9:40 PM + * + * @author Jeroen Meulemeester + */ +public class DwarvenGoldmineFacadeTest { + + /** + * The mocked standard out {@link PrintStream}, required since the actions on the gold mine facade + * don't have any influence on any other accessible objects, except for writing to std-out using + * {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Test a complete day cycle in the gold mine by executing all three different steps: {@link + * DwarvenGoldmineFacade#startNewDay()}, {@link DwarvenGoldmineFacade#digOutGold()} and {@link + * DwarvenGoldmineFacade#endDay()}. + * + * See if the workers are doing what's expected from them on each step. + */ + @Test + public void testFullWorkDay() { + final DwarvenGoldmineFacade goldMine = new DwarvenGoldmineFacade(); + goldMine.startNewDay(); + + // On the start of a day, all workers should wake up ... + verify(this.stdOutMock, times(1)).println(eq("Dwarf gold digger wakes up.")); + verify(this.stdOutMock, times(1)).println(eq("Dwarf cart operator wakes up.")); + verify(this.stdOutMock, times(1)).println(eq("Dwarven tunnel digger wakes up.")); + + // ... and go to the mine + verify(this.stdOutMock, times(1)).println(eq("Dwarf gold digger goes to the mine.")); + verify(this.stdOutMock, times(1)).println(eq("Dwarf cart operator goes to the mine.")); + verify(this.stdOutMock, times(1)).println(eq("Dwarven tunnel digger goes to the mine.")); + + // No other actions were invoked, so the workers shouldn't have done (printed) anything else + verifyNoMoreInteractions(this.stdOutMock); + + // Now do some actual work, start digging gold! + goldMine.digOutGold(); + + // Since we gave the dig command, every worker should be doing it's job ... + verify(this.stdOutMock, times(1)).println(eq("Dwarf gold digger digs for gold.")); + verify(this.stdOutMock, times(1)).println(eq("Dwarf cart operator moves gold chunks out of the mine.")); + verify(this.stdOutMock, times(1)).println(eq("Dwarven tunnel digger creates another promising tunnel.")); + + // Again, they shouldn't be doing anything else. + verifyNoMoreInteractions(this.stdOutMock); + + // Enough gold, lets end the day. + goldMine.endDay(); + + // Check if the workers go home ... + verify(this.stdOutMock, times(1)).println(eq("Dwarf gold digger goes home.")); + verify(this.stdOutMock, times(1)).println(eq("Dwarf cart operator goes home.")); + verify(this.stdOutMock, times(1)).println(eq("Dwarven tunnel digger goes home.")); + + // ... and go to sleep. We need well rested workers the next day :) + verify(this.stdOutMock, times(1)).println(eq("Dwarf gold digger goes to sleep.")); + verify(this.stdOutMock, times(1)).println(eq("Dwarf cart operator goes to sleep.")); + verify(this.stdOutMock, times(1)).println(eq("Dwarven tunnel digger goes to sleep.")); + + // Every worker should be sleeping now, no other actions allowed + verifyNoMoreInteractions(this.stdOutMock); + } + +} diff --git a/factory-kit/README.md b/factory-kit/README.md new file mode 100644 index 000000000..c25701047 --- /dev/null +++ b/factory-kit/README.md @@ -0,0 +1,28 @@ +--- +layout: pattern +title: Factory Kit +folder: factory-kit +permalink: /patterns/factory-kit/ +categories: Creational +tags: + - Java + - Difficulty-Beginner + - Functional +--- + +## Intent +Define a factory of immutable content with separated builder and factory interfaces. + +![alt text](./etc/factory-kit.png "Factory Kit") + +## Applicability +Use the Factory Kit pattern when + +* a class can't anticipate the class of objects it must create +* you just want a new instance of a custom builder instead of the global one +* you explicitly want to define types of objects, that factory can build +* you want a separated builder and creator interface + +## Credits + +* [Design Pattern Reloaded by Remi Forax: ](https://www.youtube.com/watch?v=-k2X7guaArU) diff --git a/factory-kit/etc/factory-kit.png b/factory-kit/etc/factory-kit.png new file mode 100644 index 000000000..7093193cb Binary files /dev/null and b/factory-kit/etc/factory-kit.png differ diff --git a/factory-kit/etc/factory-kit.ucls b/factory-kit/etc/factory-kit.ucls new file mode 100644 index 000000000..403fb7e27 --- /dev/null +++ b/factory-kit/etc/factory-kit.ucls @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/factory-kit/pom.xml b/factory-kit/pom.xml new file mode 100644 index 000000000..6c936de5c --- /dev/null +++ b/factory-kit/pom.xml @@ -0,0 +1,48 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + factory-kit + + + junit + junit + test + + + org.mockito + mockito-core + test + + + \ No newline at end of file diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/App.java b/factory-kit/src/main/java/com/iluwatar/factorykit/App.java new file mode 100644 index 000000000..f27bee170 --- /dev/null +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/App.java @@ -0,0 +1,54 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factorykit; + +/** + * Factory-kit is a creational pattern which defines a factory of immutable content + * with separated builder and factory interfaces to deal with the problem of + * creating one of the objects specified directly in the factory-kit instance. + * + *

+ * In the given example {@link WeaponFactory} represents the factory-kit, that contains + * four {@link Builder}s for creating new objects of + * the classes implementing {@link Weapon} interface. + *
Each of them can be called with {@link WeaponFactory#create(WeaponType)} method, with + * an input representing an instance of {@link WeaponType} that needs to + * be mapped explicitly with desired class type in the factory instance. + */ +public class App { + /** + * Program entry point. + * + * @param args @param args command line args + */ + public static void main(String[] args) { + WeaponFactory factory = WeaponFactory.factory(builder -> { + builder.add(WeaponType.SWORD, Sword::new); + builder.add(WeaponType.AXE, Axe::new); + builder.add(WeaponType.SPEAR, Spear::new); + builder.add(WeaponType.BOW, Bow::new); + }); + Weapon axe = factory.create(WeaponType.AXE); + System.out.println(axe); + } +} diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/Axe.java b/factory-kit/src/main/java/com/iluwatar/factorykit/Axe.java new file mode 100644 index 000000000..826a1f9ec --- /dev/null +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/Axe.java @@ -0,0 +1,30 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factorykit; + +public class Axe implements Weapon { + @Override + public String toString() { + return "Axe"; + } +} diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/Bow.java b/factory-kit/src/main/java/com/iluwatar/factorykit/Bow.java new file mode 100644 index 000000000..5aa952c3d --- /dev/null +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/Bow.java @@ -0,0 +1,30 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factorykit; + +public class Bow implements Weapon { + @Override + public String toString() { + return "Bow"; + } +} diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/Builder.java b/factory-kit/src/main/java/com/iluwatar/factorykit/Builder.java new file mode 100644 index 000000000..1049c7b6f --- /dev/null +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/Builder.java @@ -0,0 +1,32 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factorykit; + +import java.util.function.Supplier; + +/** + * Functional interface that allows adding builder with name to the factory. + */ +public interface Builder { + void add(WeaponType name, Supplier supplier); +} diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/Spear.java b/factory-kit/src/main/java/com/iluwatar/factorykit/Spear.java new file mode 100644 index 000000000..c32811e8c --- /dev/null +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/Spear.java @@ -0,0 +1,30 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factorykit; + +public class Spear implements Weapon { + @Override + public String toString() { + return "Spear"; + } +} diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/Sword.java b/factory-kit/src/main/java/com/iluwatar/factorykit/Sword.java new file mode 100644 index 000000000..208cd6bbb --- /dev/null +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/Sword.java @@ -0,0 +1,30 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factorykit; + +public class Sword implements Weapon { + @Override + public String toString() { + return "Sword"; + } +} diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/Weapon.java b/factory-kit/src/main/java/com/iluwatar/factorykit/Weapon.java new file mode 100644 index 000000000..3d668e352 --- /dev/null +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/Weapon.java @@ -0,0 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factorykit; + +/** + * Interface representing weapon. + */ +public interface Weapon { +} diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponFactory.java b/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponFactory.java new file mode 100644 index 000000000..80a6fd9d3 --- /dev/null +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponFactory.java @@ -0,0 +1,55 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factorykit; + +import java.util.HashMap; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * Functional interface, an example of the factory-kit design pattern. + *
Instance created locally gives an opportunity to strictly define + * which objects types the instance of a factory will be able to create. + *
Factory is a placeholder for {@link Builder}s + * with {@link WeaponFactory#create(WeaponType)} method to initialize new objects. + */ +public interface WeaponFactory { + + /** + * Creates an instance of the given type. + * @param name representing enum of an object type to be created. + * @return new instance of a requested class implementing {@link Weapon} interface. + */ + Weapon create(WeaponType name); + + /** + * Creates factory - placeholder for specified {@link Builder}s. + * @param consumer for the new builder to the factory. + * @return factory with specified {@link Builder}s + */ + static WeaponFactory factory(Consumer consumer) { + HashMap> map = new HashMap<>(); + consumer.accept(map::put); + return name -> map.get(name).get(); + } +} diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponType.java b/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponType.java new file mode 100644 index 000000000..283f252de --- /dev/null +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponType.java @@ -0,0 +1,30 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factorykit; + +/** + * Enumerates {@link Weapon} types + */ +public enum WeaponType { + SWORD, AXE, BOW, SPEAR +} diff --git a/factory-kit/src/test/java/com/iluwatar/factorykit/app/AppTest.java b/factory-kit/src/test/java/com/iluwatar/factorykit/app/AppTest.java new file mode 100644 index 000000000..036326d97 --- /dev/null +++ b/factory-kit/src/test/java/com/iluwatar/factorykit/app/AppTest.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factorykit.app; + +import com.iluwatar.factorykit.App; +import org.junit.Test; + +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} + diff --git a/factory-kit/src/test/java/com/iluwatar/factorykit/factorykit/FactoryKitTest.java b/factory-kit/src/test/java/com/iluwatar/factorykit/factorykit/FactoryKitTest.java new file mode 100644 index 000000000..c57bee3e3 --- /dev/null +++ b/factory-kit/src/test/java/com/iluwatar/factorykit/factorykit/FactoryKitTest.java @@ -0,0 +1,81 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factorykit.factorykit; + +import com.iluwatar.factorykit.*; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class FactoryKitTest { + + private WeaponFactory factory; + + @Before + public void init() { + factory = WeaponFactory.factory(builder -> { + builder.add(WeaponType.SPEAR, Spear::new); + builder.add(WeaponType.AXE, Axe::new); + builder.add(WeaponType.SWORD, Sword::new); + }); + } + + /** + * Testing {@link WeaponFactory} to produce a SPEAR asserting that the Weapon is an instance of {@link Spear} + */ + @Test + public void testSpearWeapon() { + Weapon weapon = factory.create(WeaponType.SPEAR); + verifyWeapon(weapon, Spear.class); + } + + /** + * Testing {@link WeaponFactory} to produce a AXE asserting that the Weapon is an instance of {@link Axe} + */ + @Test + public void testAxeWeapon() { + Weapon weapon = factory.create(WeaponType.AXE); + verifyWeapon(weapon, Axe.class); + } + + + /** + * Testing {@link WeaponFactory} to produce a SWORD asserting that the Weapon is an instance of {@link Sword} + */ + @Test + public void testWeapon() { + Weapon weapon = factory.create(WeaponType.SWORD); + verifyWeapon(weapon, Sword.class); + } + + /** + * This method asserts that the weapon object that is passed is an instance of the clazz + * + * @param weapon weapon object which is to be verified + * @param clazz expected class of the weapon + */ + private void verifyWeapon(Weapon weapon, Class clazz) { + assertTrue("Weapon must be an object of: " + clazz.getName(), clazz.isInstance(weapon)); + } +} diff --git a/factory-method/README.md b/factory-method/README.md new file mode 100644 index 000000000..05549cf4f --- /dev/null +++ b/factory-method/README.md @@ -0,0 +1,38 @@ +--- +layout: pattern +title: Factory Method +folder: factory-method +permalink: /patterns/factory-method/ +categories: Creational +tags: + - Java + - Difficulty-Beginner + - Gang Of Four +--- + +## Also known as +Virtual Constructor + +## Intent +Define an interface for creating an object, but let subclasses +decide which class to instantiate. Factory Method lets a class defer +instantiation to subclasses. + +![alt text](./etc/factory-method_1.png "Factory Method") + +## Applicability +Use the Factory Method pattern when + +* a class can't anticipate the class of objects it must create +* a class wants its subclasses to specify the objects it creates +* classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate + +## Known uses + +* java.util.Calendar +* java.util.ResourceBundle +* java.text.NumberFormat#getInstance() + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/factory-method/etc/factory-method.ucls b/factory-method/etc/factory-method.ucls index 6f270fd18..562437995 100644 --- a/factory-method/etc/factory-method.ucls +++ b/factory-method/etc/factory-method.ucls @@ -1,8 +1,8 @@ - - + @@ -20,8 +21,9 @@ - + @@ -29,8 +31,8 @@ - + @@ -38,8 +40,8 @@ - + @@ -47,8 +49,8 @@ - + @@ -56,8 +58,8 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + diff --git a/factory-method/pom.xml b/factory-method/pom.xml index 9f4c79c15..5081ecf99 100644 --- a/factory-method/pom.xml +++ b/factory-method/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT factory-method @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/factory-method/src/main/java/com/iluwatar/factory/method/App.java b/factory-method/src/main/java/com/iluwatar/factory/method/App.java new file mode 100644 index 000000000..cd7a6e6e7 --- /dev/null +++ b/factory-method/src/main/java/com/iluwatar/factory/method/App.java @@ -0,0 +1,77 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factory.method; + +/** + * + * The Factory Method is a creational design pattern which uses factory methods to deal with the + * problem of creating objects without specifying the exact class of object that will be created. + * This is done by creating objects via calling a factory method either specified in an interface + * and implemented by child classes, or implemented in a base class and optionally overridden by + * derived classes—rather than by calling a constructor. + *

+ * In this Factory Method example we have an interface ({@link Blacksmith}) with a method for + * creating objects ({@link Blacksmith#manufactureWeapon}). The concrete subclasses ( + * {@link OrcBlacksmith}, {@link ElfBlacksmith}) then override the method to produce objects of + * their liking. + * + */ +public class App { + + private final Blacksmith blacksmith; + + /** + * Creates an instance of App which will use blacksmith to manufacture + * the weapons for war. + * App is unaware which concrete implementation of {@link Blacksmith} it is using. + * The decision of which blacksmith implementation to use may depend on configuration, or + * the type of rival in war. + * @param blacksmith a non-null implementation of blacksmith + */ + public App(Blacksmith blacksmith) { + this.blacksmith = blacksmith; + } + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + // Lets go to war with Orc weapons + App app = new App(new OrcBlacksmith()); + app.manufactureWeapons(); + + // Lets go to war with Elf weapons + app = new App(new ElfBlacksmith()); + app.manufactureWeapons(); + } + + private void manufactureWeapons() { + Weapon weapon; + weapon = blacksmith.manufactureWeapon(WeaponType.SPEAR); + System.out.println(weapon); + weapon = blacksmith.manufactureWeapon(WeaponType.AXE); + System.out.println(weapon); + } +} diff --git a/factory-method/src/main/java/com/iluwatar/factory/method/Blacksmith.java b/factory-method/src/main/java/com/iluwatar/factory/method/Blacksmith.java new file mode 100644 index 000000000..9d90bebbc --- /dev/null +++ b/factory-method/src/main/java/com/iluwatar/factory/method/Blacksmith.java @@ -0,0 +1,34 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factory.method; + +/** + * + * The interface containing method for producing objects. + * + */ +public interface Blacksmith { + + Weapon manufactureWeapon(WeaponType weaponType); + +} diff --git a/factory-method/src/main/java/com/iluwatar/factory/method/ElfBlacksmith.java b/factory-method/src/main/java/com/iluwatar/factory/method/ElfBlacksmith.java new file mode 100644 index 000000000..52844691f --- /dev/null +++ b/factory-method/src/main/java/com/iluwatar/factory/method/ElfBlacksmith.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factory.method; + +/** + * + * Concrete subclass for creating new objects. + * + */ +public class ElfBlacksmith implements Blacksmith { + + public Weapon manufactureWeapon(WeaponType weaponType) { + return new ElfWeapon(weaponType); + } + +} diff --git a/factory-method/src/main/java/com/iluwatar/factory/method/ElfWeapon.java b/factory-method/src/main/java/com/iluwatar/factory/method/ElfWeapon.java new file mode 100644 index 000000000..c06674d49 --- /dev/null +++ b/factory-method/src/main/java/com/iluwatar/factory/method/ElfWeapon.java @@ -0,0 +1,45 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factory.method; + +/** + * ElfWeapon. + */ +public class ElfWeapon implements Weapon { + + private WeaponType weaponType; + + public ElfWeapon(WeaponType weaponType) { + this.weaponType = weaponType; + } + + @Override + public String toString() { + return "Elven " + weaponType; + } + + @Override + public WeaponType getWeaponType() { + return weaponType; + } +} diff --git a/factory-method/src/main/java/com/iluwatar/factory/method/OrcBlacksmith.java b/factory-method/src/main/java/com/iluwatar/factory/method/OrcBlacksmith.java new file mode 100644 index 000000000..75247f4a2 --- /dev/null +++ b/factory-method/src/main/java/com/iluwatar/factory/method/OrcBlacksmith.java @@ -0,0 +1,35 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factory.method; + +/** + * + * Concrete subclass for creating new objects. + * + */ +public class OrcBlacksmith implements Blacksmith { + + public Weapon manufactureWeapon(WeaponType weaponType) { + return new OrcWeapon(weaponType); + } +} diff --git a/factory-method/src/main/java/com/iluwatar/factory/method/OrcWeapon.java b/factory-method/src/main/java/com/iluwatar/factory/method/OrcWeapon.java new file mode 100644 index 000000000..abae770ed --- /dev/null +++ b/factory-method/src/main/java/com/iluwatar/factory/method/OrcWeapon.java @@ -0,0 +1,45 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factory.method; + +/** + * OrcWeapon. + */ +public class OrcWeapon implements Weapon { + + private WeaponType weaponType; + + public OrcWeapon(WeaponType weaponType) { + this.weaponType = weaponType; + } + + @Override + public String toString() { + return "Orcish " + weaponType; + } + + @Override + public WeaponType getWeaponType() { + return weaponType; + } +} diff --git a/factory-method/src/main/java/com/iluwatar/factory/method/Weapon.java b/factory-method/src/main/java/com/iluwatar/factory/method/Weapon.java new file mode 100644 index 000000000..d9c8cac0c --- /dev/null +++ b/factory-method/src/main/java/com/iluwatar/factory/method/Weapon.java @@ -0,0 +1,32 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factory.method; + +/** + * Weapon interface. + */ +public interface Weapon { + + WeaponType getWeaponType(); + +} diff --git a/factory-method/src/main/java/com/iluwatar/factory/method/WeaponType.java b/factory-method/src/main/java/com/iluwatar/factory/method/WeaponType.java new file mode 100644 index 000000000..34921ae5c --- /dev/null +++ b/factory-method/src/main/java/com/iluwatar/factory/method/WeaponType.java @@ -0,0 +1,44 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factory.method; + +/** + * + * WeaponType enumeration + * + */ +public enum WeaponType { + + SHORT_SWORD("short sword"), SPEAR("spear"), AXE("axe"), UNDEFINED(""); + + private String title; + + WeaponType(String title) { + this.title = title; + } + + @Override + public String toString() { + return title; + } +} diff --git a/factory-method/src/main/java/com/iluwatar/factorymethod/App.java b/factory-method/src/main/java/com/iluwatar/factorymethod/App.java deleted file mode 100644 index 90d14dfb0..000000000 --- a/factory-method/src/main/java/com/iluwatar/factorymethod/App.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.iluwatar.factorymethod; - -/** - * - * In Factory Method we have an interface (Blacksmith) with a method for - * creating objects (manufactureWeapon). The concrete subclasses (OrcBlacksmith, - * ElfBlacksmith) then override the method to produce objects of their liking. - * - */ -public class App { - - public static void main(String[] args) { - Blacksmith blacksmith; - Weapon weapon; - - blacksmith = new OrcBlacksmith(); - weapon = blacksmith.manufactureWeapon(WeaponType.SPEAR); - System.out.println(weapon); - weapon = blacksmith.manufactureWeapon(WeaponType.AXE); - System.out.println(weapon); - - blacksmith = new ElfBlacksmith(); - weapon = blacksmith.manufactureWeapon(WeaponType.SHORT_SWORD); - System.out.println(weapon); - weapon = blacksmith.manufactureWeapon(WeaponType.SPEAR); - System.out.println(weapon); - } -} diff --git a/factory-method/src/main/java/com/iluwatar/factorymethod/Blacksmith.java b/factory-method/src/main/java/com/iluwatar/factorymethod/Blacksmith.java deleted file mode 100644 index b18a5c6df..000000000 --- a/factory-method/src/main/java/com/iluwatar/factorymethod/Blacksmith.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.iluwatar.factorymethod; - -/** - * - * The interface containing method for producing objects. - * - */ -public interface Blacksmith { - - Weapon manufactureWeapon(WeaponType weaponType); - -} diff --git a/factory-method/src/main/java/com/iluwatar/factorymethod/ElfBlacksmith.java b/factory-method/src/main/java/com/iluwatar/factorymethod/ElfBlacksmith.java deleted file mode 100644 index 21c6aacdf..000000000 --- a/factory-method/src/main/java/com/iluwatar/factorymethod/ElfBlacksmith.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.iluwatar.factorymethod; - -/** - * - * Concrete subclass for creating new objects. - * - */ -public class ElfBlacksmith implements Blacksmith { - - public Weapon manufactureWeapon(WeaponType weaponType) { - return new ElfWeapon(weaponType); - } - -} diff --git a/factory-method/src/main/java/com/iluwatar/factorymethod/ElfWeapon.java b/factory-method/src/main/java/com/iluwatar/factorymethod/ElfWeapon.java deleted file mode 100644 index 7e90f3b38..000000000 --- a/factory-method/src/main/java/com/iluwatar/factorymethod/ElfWeapon.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.iluwatar.factorymethod; - -public class ElfWeapon implements Weapon { - - private WeaponType weaponType; - - public ElfWeapon(WeaponType weaponType) { - this.weaponType = weaponType; - } - - @Override - public String toString() { - return "Elven " + weaponType; - } - -} diff --git a/factory-method/src/main/java/com/iluwatar/factorymethod/OrcBlacksmith.java b/factory-method/src/main/java/com/iluwatar/factorymethod/OrcBlacksmith.java deleted file mode 100644 index 297dd6b98..000000000 --- a/factory-method/src/main/java/com/iluwatar/factorymethod/OrcBlacksmith.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.iluwatar.factorymethod; - -/** - * - * Concrete subclass for creating new objects. - * - */ -public class OrcBlacksmith implements Blacksmith { - - public Weapon manufactureWeapon(WeaponType weaponType) { - return new OrcWeapon(weaponType); - } - -} diff --git a/factory-method/src/main/java/com/iluwatar/factorymethod/OrcWeapon.java b/factory-method/src/main/java/com/iluwatar/factorymethod/OrcWeapon.java deleted file mode 100644 index 2d7b19adb..000000000 --- a/factory-method/src/main/java/com/iluwatar/factorymethod/OrcWeapon.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.iluwatar.factorymethod; - -public class OrcWeapon implements Weapon { - - private WeaponType weaponType; - - public OrcWeapon(WeaponType weaponType) { - this.weaponType = weaponType; - } - - @Override - public String toString() { - return "Orcish " + weaponType; - } - -} diff --git a/factory-method/src/main/java/com/iluwatar/factorymethod/Weapon.java b/factory-method/src/main/java/com/iluwatar/factorymethod/Weapon.java deleted file mode 100644 index 3a9346d03..000000000 --- a/factory-method/src/main/java/com/iluwatar/factorymethod/Weapon.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.iluwatar.factorymethod; - -public interface Weapon { - -} diff --git a/factory-method/src/main/java/com/iluwatar/factorymethod/WeaponType.java b/factory-method/src/main/java/com/iluwatar/factorymethod/WeaponType.java deleted file mode 100644 index 45e1f82de..000000000 --- a/factory-method/src/main/java/com/iluwatar/factorymethod/WeaponType.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.iluwatar.factorymethod; - -public enum WeaponType { - - SHORT_SWORD("short sword"), SPEAR("spear"), AXE("axe"), UNDEFINED(""); - - private String title; - - WeaponType(String title) { - this.title = title; - } - - @Override - public String toString() { - return title; - } -} diff --git a/factory-method/src/test/java/com/iluwatar/factory/method/AppTest.java b/factory-method/src/test/java/com/iluwatar/factory/method/AppTest.java new file mode 100644 index 000000000..818ee96cd --- /dev/null +++ b/factory-method/src/test/java/com/iluwatar/factory/method/AppTest.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factory.method; + +import org.junit.Test; + +import java.io.IOException; + +/** + * Tests that Factory Method example runs without errors. + */ +public class AppTest { + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } +} diff --git a/factory-method/src/test/java/com/iluwatar/factory/method/FactoryMethodTest.java b/factory-method/src/test/java/com/iluwatar/factory/method/FactoryMethodTest.java new file mode 100644 index 000000000..2f8d1c9bb --- /dev/null +++ b/factory-method/src/test/java/com/iluwatar/factory/method/FactoryMethodTest.java @@ -0,0 +1,101 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factory.method; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * The Factory Method is a creational design pattern which uses factory methods to deal with the + * problem of creating objects without specifying the exact class of object that will be created. + * This is done by creating objects via calling a factory method either specified in an interface + * and implemented by child classes, or implemented in a base class and optionally overridden by + * derived classes—rather than by calling a constructor. + * + *

Factory produces the object of its liking. + * The weapon {@link Weapon} manufactured by the + * blacksmith depends on the kind of factory implementation it is referring to. + *

+ */ +public class FactoryMethodTest { + + /** + * Testing {@link OrcBlacksmith} to produce a SPEAR asserting that the Weapon is an instance + * of {@link OrcWeapon}. + */ + @Test + public void testOrcBlacksmithWithSpear() { + Blacksmith blacksmith = new OrcBlacksmith(); + Weapon weapon = blacksmith.manufactureWeapon(WeaponType.SPEAR); + verifyWeapon(weapon, WeaponType.SPEAR, OrcWeapon.class); + } + + /** + * Testing {@link OrcBlacksmith} to produce a AXE asserting that the Weapon is an instance + * of {@link OrcWeapon}. + */ + @Test + public void testOrcBlacksmithWithAxe() { + Blacksmith blacksmith = new OrcBlacksmith(); + Weapon weapon = blacksmith.manufactureWeapon(WeaponType.AXE); + verifyWeapon(weapon, WeaponType.AXE, OrcWeapon.class); + } + + /** + * Testing {@link ElfBlacksmith} to produce a SHORT_SWORD asserting that the Weapon is an + * instance of {@link ElfWeapon}. + */ + @Test + public void testElfBlacksmithWithShortSword() { + Blacksmith blacksmith = new ElfBlacksmith(); + Weapon weapon = blacksmith.manufactureWeapon(WeaponType.SHORT_SWORD); + verifyWeapon(weapon, WeaponType.SHORT_SWORD, ElfWeapon.class); + } + + /** + * Testing {@link ElfBlacksmith} to produce a SPEAR asserting that the Weapon is an instance + * of {@link ElfWeapon}. + */ + @Test + public void testElfBlacksmithWithSpear() { + Blacksmith blacksmith = new ElfBlacksmith(); + Weapon weapon = blacksmith.manufactureWeapon(WeaponType.SPEAR); + verifyWeapon(weapon, WeaponType.SPEAR, ElfWeapon.class); + } + + /** + * This method asserts that the weapon object that is passed is an instance of the clazz and the + * weapon is of type expectedWeaponType. + * + * @param weapon weapon object which is to be verified + * @param expectedWeaponType expected WeaponType of the weapon + * @param clazz expected class of the weapon + */ + private void verifyWeapon(Weapon weapon, WeaponType expectedWeaponType, Class clazz) { + assertTrue("Weapon must be an object of: " + clazz.getName(), clazz.isInstance(weapon)); + assertEquals("Weapon must be of weaponType: " + clazz.getName(), expectedWeaponType, + weapon.getWeaponType()); + } +} diff --git a/factory-method/src/test/java/com/iluwatar/factorymethod/AppTest.java b/factory-method/src/test/java/com/iluwatar/factorymethod/AppTest.java deleted file mode 100644 index 0abf55c2d..000000000 --- a/factory-method/src/test/java/com/iluwatar/factorymethod/AppTest.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.iluwatar.factorymethod; - -import org.junit.Test; - -import com.iluwatar.factorymethod.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} diff --git a/faq.md b/faq.md new file mode 100644 index 000000000..69f7b795e --- /dev/null +++ b/faq.md @@ -0,0 +1,67 @@ +--- +layout: page +title: FAQ +permalink: /faq/ +icon: fa-question +page-index: 1 +--- + +### Q1: What is the difference between State and Strategy patterns? {#Q1} + +While the implementation is similar they solve different problems. The State +pattern deals with what state an object is in - it encapsulates state-dependent +behavior. +The Strategy pattern deals with how an object performs a certain task - it +encapsulates an algorithm. + +### Q2: What is the difference between Strategy and Template Method patterns? {#Q2} + +In Template Method the algorithm is chosen at compile time via inheritance. +With Strategy pattern the algorithm is chosen at runtime via composition. + +### Q3: What is the difference between Proxy and Decorator patterns? {#Q3} + +The difference is the intent of the patterns. While Proxy controls access to +the object Decorator is used to add responsibilities to the object. + +### Q4: What is the difference between Chain of Responsibility and Intercepting Filter patterns? {#Q4} + +While the implementations look similar there are differences. The Chain of +Responsibility forms a chain of request processors and the processors are then +executed one by one until the correct processor is found. In Intercepting +Filter the chain is constructed from filters and the whole chain is always +executed. + +### Q5: What is the difference between Visitor and Double Dispatch patterns? {#Q5} + +The Visitor pattern is a means of adding a new operation to existing classes. +Double dispatch is a means of dispatching function calls with respect to two +polymorphic types, rather than a single polymorphic type, which is what +languages like C++ and Java _do not_ support directly. + +### Q6: What are the differences between Flyweight and Object Pool patterns? {#Q6} + +They differ in the way they are used. + +Pooled objects can simultaneously be used by a single "client" only. For that, +a pooled object must be checked out from the pool, then it can be used by a +client, and then the client must return the object back to the pool. Multiple +instances of identical objects may exist, up to the maximal capacity of the +pool. + +In contrast, a Flyweight object is singleton, and it can be used simultaneously +by multiple clients. + +As for concurrent access, pooled objects can be mutable and they usually don't +need to be thread safe, as typically, only one thread is going to use a +specific instance at the same time. Flyweight must either be immutable (the +best option), or implement thread safety. + +As for performance and scalability, pools can become bottlenecks, if all the +pooled objects are in use and more clients need them, threads will become +blocked waiting for available object from the pool. This is not the case with +Flyweight. + +### Q7: What are the differences between FluentInterface and Builder patterns? {#Q7} + +Fluent interfaces are sometimes confused with the Builder pattern, because they share method chaining and a fluent usage. However, fluent interfaces are not primarily used to create shared (mutable) objects, but to configure complex objects without having to respecify the target object on every property change. diff --git a/feature-toggle/README.md b/feature-toggle/README.md new file mode 100644 index 000000000..51747ac09 --- /dev/null +++ b/feature-toggle/README.md @@ -0,0 +1,32 @@ +--- +layout: pattern +title: Feature Toggle +folder: feature-toggle +permalink: /patterns/feature-toggle/ +categories: Behavioral +tags: + - Java + - Difficulty-Beginner +--- + +## Also known as +Feature Flag + +## Intent +Used to switch code execution paths based on properties or groupings. Allowing new features to be released, tested +and rolled out. Allowing switching back to the older feature quickly if needed. It should be noted that this pattern, +can easily introduce code complexity. There is also cause for concern that the old feature that the toggle is eventually +going to phase out is never removed, causing redundant code smells and increased maintainability. + +![alt text](./etc/feature-toggle.png "Feature Toggle") + +## Applicability +Use the Feature Toogle pattern when + +* Giving different features to different users. +* Rolling out a new feature incrementally. +* Switching between development and production environments. + +## Credits + +* [Martin Fowler 29 October 2010 (2010-10-29).](http://martinfowler.com/bliki/FeatureToggle.html) \ No newline at end of file diff --git a/feature-toggle/etc/feature-toggle.png b/feature-toggle/etc/feature-toggle.png new file mode 100644 index 000000000..5c118e57e Binary files /dev/null and b/feature-toggle/etc/feature-toggle.png differ diff --git a/feature-toggle/etc/feature-toggle.ucls b/feature-toggle/etc/feature-toggle.ucls new file mode 100644 index 000000000..538d3f416 --- /dev/null +++ b/feature-toggle/etc/feature-toggle.ucls @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/feature-toggle/pom.xml b/feature-toggle/pom.xml new file mode 100644 index 000000000..78c182af9 --- /dev/null +++ b/feature-toggle/pom.xml @@ -0,0 +1,48 @@ + + + + + + java-design-patterns + com.iluwatar + 1.13.0-SNAPSHOT + + 4.0.0 + + feature-toggle + + + + + junit + junit + test + + + + \ No newline at end of file diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/App.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/App.java new file mode 100644 index 000000000..debe99580 --- /dev/null +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/App.java @@ -0,0 +1,96 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.featuretoggle; + +import com.iluwatar.featuretoggle.pattern.Service; +import com.iluwatar.featuretoggle.pattern.propertiesversion.PropertiesFeatureToggleVersion; +import com.iluwatar.featuretoggle.user.User; +import com.iluwatar.featuretoggle.user.UserGroup; + +import java.util.Properties; + +/** + * The Feature Toggle pattern allows for complete code executions to be turned on or off with ease. This allows features + * to be controlled by either dynamic methods just as {@link User} information or by {@link Properties}. In the App + * below there are two examples. Firstly the {@link Properties} version of the feature toggle, where the enhanced + * version of the welcome message which is personalised is turned either on or off at instance creation. This method + * is not as dynamic as the {@link User} driven version where the feature of the personalised welcome message is + * dependant on the {@link UserGroup} the {@link User} is in. So if the user is a memeber of the + * {@link UserGroup#isPaid(User)} then they get an ehanced version of the welcome message. + * + * Note that this pattern can easily introduce code complexity, and if not kept in check can result in redundant + * unmaintained code within the codebase. + * + */ +public class App { + + /** + * Block 1 shows the {@link PropertiesFeatureToggleVersion} being run with {@link Properties} setting the feature + * toggle to enabled. + * + * Block 2 shows the {@link PropertiesFeatureToggleVersion} being run with {@link Properties} setting the feature + * toggle to disabled. Notice the difference with the printed welcome message the username is not included. + * + * Block 3 shows the {@link com.iluwatar.featuretoggle.pattern.tieredversion.TieredFeatureToggleVersion} being + * set up with two users on who is on the free level, while the other is on the paid level. When the + * {@link Service#getWelcomeMessage(User)} is called with the paid {@link User} note that the welcome message + * contains their username, while the same service call with the free tier user is more generic. No username is + * printed. + * + * @see User + * @see UserGroup + * @see Service + * @see PropertiesFeatureToggleVersion + * @see com.iluwatar.featuretoggle.pattern.tieredversion.TieredFeatureToggleVersion; + */ + public static void main(String[] args) { + + final Properties properties = new Properties(); + properties.put("enhancedWelcome", true); + Service service = new PropertiesFeatureToggleVersion(properties); + final String welcomeMessage = service.getWelcomeMessage(new User("Jamie No Code")); + System.out.println(welcomeMessage); + + // --------------------------------------------- + + final Properties turnedOff = new Properties(); + turnedOff.put("enhancedWelcome", false); + Service turnedOffService = new PropertiesFeatureToggleVersion(turnedOff); + final String welcomeMessageturnedOff = turnedOffService.getWelcomeMessage(new User("Jamie No Code")); + System.out.println(welcomeMessageturnedOff); + + // -------------------------------------------- + + final User paidUser = new User("Jamie Coder"); + final User freeUser = new User("Alan Defect"); + + UserGroup.addUserToPaidGroup(paidUser); + UserGroup.addUserToFreeGroup(freeUser); + + final String welcomeMessagePaidUser = service.getWelcomeMessage(paidUser); + final String welcomeMessageFreeUser = service.getWelcomeMessage(freeUser); + System.out.println(welcomeMessageFreeUser); + System.out.println(welcomeMessagePaidUser); + } +} diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java new file mode 100644 index 000000000..d2542b2b7 --- /dev/null +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java @@ -0,0 +1,54 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.featuretoggle.pattern; + +import com.iluwatar.featuretoggle.user.User; + +/** + * Simple interfaces to allow the calling of the method to generate the welcome message for a given user. While there is + * a helper method to gather the the status of the feature toggle. In some cases there is no need for the + * {@link Service#isEnhanced()} in {@link com.iluwatar.featuretoggle.pattern.tieredversion.TieredFeatureToggleVersion} + * where the toggle is determined by the actual {@link User}. + * + * @see com.iluwatar.featuretoggle.pattern.propertiesversion.PropertiesFeatureToggleVersion + * @see com.iluwatar.featuretoggle.pattern.tieredversion.TieredFeatureToggleVersion + * @see User + */ +public interface Service { + + /** + * Generates a welcome message for the passed user. + * + * @param user the {@link User} to be used if the message is to be personalised. + * @return Generated {@link String} welcome message + */ + String getWelcomeMessage(User user); + + /** + * Returns if the welcome message to be displayed will be the enhanced version. + * + * @return Boolean {@value true} if enhanced. + */ + boolean isEnhanced(); + +} diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java new file mode 100644 index 000000000..761d7d39a --- /dev/null +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java @@ -0,0 +1,100 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.featuretoggle.pattern.propertiesversion; + +import com.iluwatar.featuretoggle.pattern.Service; +import com.iluwatar.featuretoggle.user.User; + +import java.util.Properties; + +/** + * This example of the Feature Toogle pattern is less dynamic version than + * {@link com.iluwatar.featuretoggle.pattern.tieredversion.TieredFeatureToggleVersion} where the feature is turned on + * or off at the time of creation of the service. This example uses simple Java {@link Properties} however it could as + * easily be done with an external configuration file loaded by Spring and so on. A good example of when to use this + * version of the feature toggle is when new features are being developed. So you could have a configuration property + * boolean named development or some sort of system environment variable. + * + * @see Service + * @see com.iluwatar.featuretoggle.pattern.tieredversion.TieredFeatureToggleVersion + * @see User + */ +public class PropertiesFeatureToggleVersion implements Service { + + private boolean isEnhanced; + + /** + * Creates an instance of {@link PropertiesFeatureToggleVersion} using the passed {@link Properties} to determine, + * the status of the feature toggle {@link PropertiesFeatureToggleVersion#isEnhanced()}. There is also some defensive + * code to ensure the {@link Properties} passed are as expected. + * + * @param properties {@link Properties} used to configure the service and toggle features. + * @throws IllegalArgumentException when the passed {@link Properties} is not as expected + * @see Properties + */ + public PropertiesFeatureToggleVersion(final Properties properties) { + if (properties == null) { + throw new IllegalArgumentException("No Properties Provided."); + } else { + try { + isEnhanced = (boolean) properties.get("enhancedWelcome"); + } catch (Exception e) { + throw new IllegalArgumentException("Invalid Enhancement Settings Provided."); + } + } + } + + /** + * Generate a welcome message based on the user being passed and the status of the feature toggle. If the enhanced + * version is enabled, then the message will be personalised with the name of the passed {@link User}. However if + * disabled then a generic version fo the message is returned. + * + * @param user the {@link User} to be displayed in the message if the enhanced version is enabled see + * {@link PropertiesFeatureToggleVersion#isEnhanced()}. If the enhanced version is enabled, then the + * message will be personalised with the name of the passed {@link User}. However if disabled then a + * generic version fo the message is returned. + * @return Resulting welcome message. + * @see User + */ + @Override + public String getWelcomeMessage(final User user) { + + if (isEnhanced()) { + return "Welcome " + user + ". You're using the enhanced welcome message."; + } + + return "Welcome to the application."; + } + + /** + * Method that checks if the welcome message to be returned is the enhanced venison or not. For this service it will + * see the value of the boolean that was set in the constructor + * {@link PropertiesFeatureToggleVersion#PropertiesFeatureToggleVersion(Properties)} + * + * @return Boolean value {@value true} if enhanced. + */ + @Override + public boolean isEnhanced() { + return isEnhanced; + } +} diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java new file mode 100644 index 000000000..124c9533f --- /dev/null +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java @@ -0,0 +1,75 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.featuretoggle.pattern.tieredversion; + +import com.iluwatar.featuretoggle.pattern.Service; +import com.iluwatar.featuretoggle.user.User; +import com.iluwatar.featuretoggle.user.UserGroup; + +/** + * This example of the Feature Toogle pattern shows how it could be implemented based on a {@link User}. Therefore + * showing its use within a tiered application where the paying users get access to different content or + * better versions of features. So in this instance a {@link User} is passed in and if they are found to be + * on the {@link UserGroup#isPaid(User)} they are welcomed with a personalised message. While the other is more + * generic. However this pattern is limited to simple examples such as the one below. + * + * @see Service + * @see User + * @see com.iluwatar.featuretoggle.pattern.propertiesversion.PropertiesFeatureToggleVersion + * @see UserGroup + */ +public class TieredFeatureToggleVersion implements Service { + + /** + * Generates a welcome message from the passed {@link User}. The resulting message depends on the group of the + * {@link User}. So if the {@link User} is in the {@link UserGroup#paidGroup} then the enhanced version of the + * welcome message will be returned where the username is displayed. + * + * @param user the {@link User} to generate the welcome message for, different messages are displayed if the user is + * in the {@link UserGroup#isPaid(User)} or {@link UserGroup#freeGroup} + * @return Resulting welcome message. + * @see User + * @see UserGroup + */ + @Override + public String getWelcomeMessage(User user) { + if (UserGroup.isPaid(user)) { + return "You're amazing " + user + ". Thanks for paying for this awesome software."; + } + + return "I suppose you can use this software."; + } + + /** + * Method that checks if the welcome message to be returned is the enhanced version. For this instance as the logic + * is driven by the user group. This method is a little redundant. However can be used to show that there is an + * enhanced version available. + * + * @return Boolean value {@value true} if enhanced. + */ + @Override + public boolean isEnhanced() { + return true; + } + +} diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java new file mode 100644 index 000000000..ce7b54b7b --- /dev/null +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java @@ -0,0 +1,49 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.featuretoggle.user; + +/** + * Used to demonstrate the purpose of the feature toggle. This class actually has nothing to do with the pattern. + */ +public class User { + + private String name; + + /** + * Default Constructor setting the username. + * + * @param name {@link String} to represent the name of the user. + */ + public User(String name) { + this.name = name; + } + + /** + * {@inheritDoc} + * @return The {@link String} representation of the User, in this case just return the name of the user. + */ + @Override + public String toString() { + return name; + } +} diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java new file mode 100644 index 000000000..c9d9fd027 --- /dev/null +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java @@ -0,0 +1,84 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.featuretoggle.user; + +import java.util.ArrayList; +import java.util.List; + +/** + * Contains the lists of users of different groups paid and free. Used to demonstrate the tiered example of feature + * toggle. Allowing certain features to be available to only certain groups of users. + * + * @see User + */ +public class UserGroup { + + private static List freeGroup = new ArrayList<>(); + private static List paidGroup = new ArrayList<>(); + + + /** + * Add the passed {@link User} to the free user group list. + * + * @param user {@link User} to be added to the free group + * @throws IllegalArgumentException when user is already added to the paid group + * @see User + */ + public static void addUserToFreeGroup(final User user) throws IllegalArgumentException { + if (paidGroup.contains(user)) { + throw new IllegalArgumentException("User all ready member of paid group."); + } else { + if (!freeGroup.contains(user)) { + freeGroup.add(user); + } + } + } + + /** + * Add the passed {@link User} to the paid user group list. + * + * @param user {@link User} to be added to the paid group + * @throws IllegalArgumentException when the user is already added to the free group + * @see User + */ + public static void addUserToPaidGroup(final User user) throws IllegalArgumentException { + if (freeGroup.contains(user)) { + throw new IllegalArgumentException("User all ready member of free group."); + } else { + if (!paidGroup.contains(user)) { + paidGroup.add(user); + } + } + } + + /** + * Method to take a {@link User} to determine if the user is in the {@link UserGroup#paidGroup}. + * + * @param user {@link User} to check if they are in the {@link UserGroup#paidGroup} + * + * @return true if the {@link User} is in {@link UserGroup#paidGroup} + */ + public static boolean isPaid(User user) { + return paidGroup.contains(user); + } +} diff --git a/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersionTest.java b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersionTest.java new file mode 100644 index 000000000..69afc9bb4 --- /dev/null +++ b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersionTest.java @@ -0,0 +1,69 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.featuretoggle.pattern.propertiesversion; + +import com.iluwatar.featuretoggle.pattern.Service; +import com.iluwatar.featuretoggle.user.User; +import org.junit.Test; + +import java.util.Properties; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class PropertiesFeatureToggleVersionTest { + + @Test(expected = IllegalArgumentException.class) + public void testNullPropertiesPassed() throws Exception { + new PropertiesFeatureToggleVersion(null); + } + + @Test(expected = IllegalArgumentException.class) + public void testNonBooleanProperty() throws Exception { + final Properties properties = new Properties(); + properties.setProperty("enhancedWelcome", "Something"); + new PropertiesFeatureToggleVersion(properties); + } + + @Test + public void testFeatureTurnedOn() throws Exception { + final Properties properties = new Properties(); + properties.put("enhancedWelcome", true); + Service service = new PropertiesFeatureToggleVersion(properties); + assertTrue(service.isEnhanced()); + final String welcomeMessage = service.getWelcomeMessage(new User("Jamie No Code")); + assertEquals("Welcome Jamie No Code. You're using the enhanced welcome message.", welcomeMessage); + } + + @Test + public void testFeatureTurnedOff() throws Exception { + final Properties properties = new Properties(); + properties.put("enhancedWelcome", false); + Service service = new PropertiesFeatureToggleVersion(properties); + assertFalse(service.isEnhanced()); + final String welcomeMessage = service.getWelcomeMessage(new User("Jamie No Code")); + assertEquals("Welcome to the application.", welcomeMessage); + } +} \ No newline at end of file diff --git a/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java new file mode 100644 index 000000000..dca1d9b82 --- /dev/null +++ b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java @@ -0,0 +1,64 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.featuretoggle.pattern.tieredversion; + +import com.iluwatar.featuretoggle.pattern.Service; +import com.iluwatar.featuretoggle.user.User; +import com.iluwatar.featuretoggle.user.UserGroup; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class TieredFeatureToggleVersionTest { + + final User paidUser = new User("Jamie Coder"); + final User freeUser = new User("Alan Defect"); + final Service service = new TieredFeatureToggleVersion(); + + @Before + public void setUp() throws Exception { + UserGroup.addUserToPaidGroup(paidUser); + UserGroup.addUserToFreeGroup(freeUser); + } + + @Test + public void testGetWelcomeMessageForPaidUser() throws Exception { + final String welcomeMessage = service.getWelcomeMessage(paidUser); + final String expected = "You're amazing Jamie Coder. Thanks for paying for this awesome software."; + assertEquals(expected, welcomeMessage); + } + + @Test + public void testGetWelcomeMessageForFreeUser() throws Exception { + final String welcomeMessage = service.getWelcomeMessage(freeUser); + final String expected = "I suppose you can use this software."; + assertEquals(expected, welcomeMessage); + } + + @Test + public void testIsEnhancedAlwaysTrueAsTiered() throws Exception { + assertTrue(service.isEnhanced()); + } +} \ No newline at end of file diff --git a/feature-toggle/src/test/java/com/iluwatar/featuretoggle/user/UserGroupTest.java b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/user/UserGroupTest.java new file mode 100644 index 000000000..6659815d3 --- /dev/null +++ b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/user/UserGroupTest.java @@ -0,0 +1,59 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.featuretoggle.user; + +import org.junit.Test; + +import static junit.framework.TestCase.assertFalse; +import static org.junit.Assert.assertTrue; + +public class UserGroupTest { + + @Test + public void testAddUserToFreeGroup() throws Exception { + User user = new User("Free User"); + UserGroup.addUserToFreeGroup(user); + assertFalse(UserGroup.isPaid(user)); + } + + @Test + public void testAddUserToPaidGroup() throws Exception { + User user = new User("Paid User"); + UserGroup.addUserToPaidGroup(user); + assertTrue(UserGroup.isPaid(user)); + } + + @Test(expected = IllegalArgumentException.class) + public void testAddUserToPaidWhenOnFree() throws Exception { + User user = new User("Paid User"); + UserGroup.addUserToFreeGroup(user); + UserGroup.addUserToPaidGroup(user); + } + + @Test(expected = IllegalArgumentException.class) + public void testAddUserToFreeWhenOnPaid() throws Exception { + User user = new User("Free User"); + UserGroup.addUserToPaidGroup(user); + UserGroup.addUserToFreeGroup(user); + } +} \ No newline at end of file diff --git a/fluentinterface/README.md b/fluentinterface/README.md new file mode 100644 index 000000000..767792da7 --- /dev/null +++ b/fluentinterface/README.md @@ -0,0 +1,44 @@ +--- +layout: pattern +title: Fluent Interface +folder: fluentinterface +permalink: /patterns/fluentinterface/ +categories: Other +tags: + - Java + - Difficulty-Intermediate + - Functional +--- + +## Intent +A fluent interface provides an easy-readable, flowing interface, that often mimics a domain specific language. Using this pattern results in code that can be read nearly as human language. + +## Implementation + +A fluent interface can be implemented using any of + + * Method Chaining - calling a method returns some object on which further methods can be called. + * Static Factory Methods and Imports + * Named parameters - can be simulated in Java using static factory methods. + +![Fluent Interface](./etc/fluentinterface.png "Fluent Interface") + +## Applicability +Use the Fluent Interface pattern when + +* you provide an API that would benefit from a DSL-like usage +* you have objects that are difficult to configure or use + +## Real world examples + +* [Java 8 Stream API](http://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams-2177646.html) +* [Google Guava FluentInterable](https://github.com/google/guava/wiki/FunctionalExplained) +* [JOOQ](http://www.jooq.org/doc/3.0/manual/getting-started/use-cases/jooq-as-a-standalone-sql-builder/) +* [Mockito](http://mockito.org/) +* [Java Hamcrest](http://code.google.com/p/hamcrest/wiki/Tutorial) + +## Credits + +* [Fluent Interface - Martin Fowler](http://www.martinfowler.com/bliki/FluentInterface.html) +* [Evolutionary architecture and emergent design: Fluent interfaces - Neal Ford](http://www.ibm.com/developerworks/library/j-eaed14/) +* [Internal DSL](http://www.infoq.com/articles/internal-dsls-java) diff --git a/fluentinterface/etc/fluentinterface.png b/fluentinterface/etc/fluentinterface.png new file mode 100644 index 000000000..611fec7c6 Binary files /dev/null and b/fluentinterface/etc/fluentinterface.png differ diff --git a/fluentinterface/etc/fluentinterface.ucls b/fluentinterface/etc/fluentinterface.ucls new file mode 100644 index 000000000..e30c45bb2 --- /dev/null +++ b/fluentinterface/etc/fluentinterface.ucls @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fluentinterface/pom.xml b/fluentinterface/pom.xml new file mode 100644 index 000000000..ca5e115d0 --- /dev/null +++ b/fluentinterface/pom.xml @@ -0,0 +1,49 @@ + + + + + java-design-patterns + com.iluwatar + 1.13.0-SNAPSHOT + + 4.0.0 + + fluentinterface + + + junit + junit + test + + + org.mockito + mockito-core + test + + + \ No newline at end of file diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/app/App.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/app/App.java new file mode 100644 index 000000000..1be2b1e70 --- /dev/null +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/app/App.java @@ -0,0 +1,131 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.fluentinterface.app; + +import static java.lang.String.valueOf; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.StringJoiner; +import java.util.function.Function; +import java.util.function.Predicate; + +import com.iluwatar.fluentinterface.fluentiterable.FluentIterable; +import com.iluwatar.fluentinterface.fluentiterable.lazy.LazyFluentIterable; +import com.iluwatar.fluentinterface.fluentiterable.simple.SimpleFluentIterable; + +/** + * The Fluent Interface pattern is useful when you want to provide an easy readable, flowing API. + * Those interfaces tend to mimic domain specific languages, so they can nearly be read as human + * languages. + *

+ * In this example two implementations of a {@link FluentIterable} interface are given. The + * {@link SimpleFluentIterable} evaluates eagerly and would be too costly for real world + * applications. The {@link LazyFluentIterable} is evaluated on termination. Their usage is + * demonstrated with a simple number list that is filtered, transformed and collected. The result is + * printed afterwards. + * + */ +public class App { + + /** + * Program entry point + */ + public static void main(String[] args) { + + List integerList = new ArrayList<>(); + integerList.addAll(Arrays.asList(1, -61, 14, -22, 18, -87, 6, 64, -82, 26, -98, 97, 45, 23, 2, + -68, 45)); + + prettyPrint("The initial list contains: ", integerList); + + List firstFiveNegatives = + SimpleFluentIterable.fromCopyOf(integerList).filter(negatives()).first(3).asList(); + prettyPrint("The first three negative values are: ", firstFiveNegatives); + + + List lastTwoPositives = + SimpleFluentIterable.fromCopyOf(integerList).filter(positives()).last(2).asList(); + prettyPrint("The last two positive values are: ", lastTwoPositives); + + SimpleFluentIterable + .fromCopyOf(integerList) + .filter(number -> number % 2 == 0) + .first() + .ifPresent( + evenNumber -> System.out.println(String.format("The first even number is: %d", + evenNumber))); + + + List transformedList = + SimpleFluentIterable.fromCopyOf(integerList).filter(negatives()).map(transformToString()) + .asList(); + prettyPrint("A string-mapped list of negative numbers contains: ", transformedList); + + + List lastTwoOfFirstFourStringMapped = + LazyFluentIterable.from(integerList).filter(positives()).first(4).last(2) + .map(number -> "String[" + valueOf(number) + "]").asList(); + prettyPrint( + "The lazy list contains the last two of the first four positive numbers mapped to Strings: ", + lastTwoOfFirstFourStringMapped); + + LazyFluentIterable + .from(integerList) + .filter(negatives()) + .first(2) + .last() + .ifPresent( + lastOfFirstTwo -> System.out.println(String.format( + "The last of the first two negatives is: %d", lastOfFirstTwo))); + } + + private static Function transformToString() { + return integer -> "String[" + valueOf(integer) + "]"; + } + + private static Predicate negatives() { + return integer -> integer < 0; + } + + private static Predicate positives() { + return integer -> integer > 0; + } + + private static void prettyPrint(String prefix, Iterable iterable) { + prettyPrint(", ", prefix, iterable); + } + + private static void prettyPrint(String delimiter, String prefix, + Iterable iterable) { + StringJoiner joiner = new StringJoiner(delimiter, prefix, "."); + Iterator iterator = iterable.iterator(); + while (iterator.hasNext()) { + joiner.add(iterator.next().toString()); + } + + System.out.println(joiner); + } +} diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java new file mode 100644 index 000000000..f6d7e2e2b --- /dev/null +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java @@ -0,0 +1,111 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.fluentinterface.fluentiterable; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * The FluentIterable is a more convenient implementation of the common iterable interface based on + * the fluent interface design pattern. This interface defines common operations, but doesn't aim to + * be complete. It was inspired by Guava's com.google.common.collect.FluentIterable. + * + * @param is the class of objects the iterable contains + */ +public interface FluentIterable extends Iterable { + + /** + * Filters the contents of Iterable using the given predicate, leaving only the ones which satisfy + * the predicate. + * + * @param predicate the condition to test with for the filtering. If the test is negative, the + * tested object is removed by the iterator. + * @return a filtered FluentIterable + */ + FluentIterable filter(Predicate predicate); + + /** + * Returns an Optional containing the first element of this iterable if present, else returns + * Optional.empty(). + * + * @return the first element after the iteration is evaluated + */ + Optional first(); + + /** + * Evaluates the iteration and leaves only the count first elements. + * + * @return the first count elements as an Iterable + */ + FluentIterable first(int count); + + /** + * Evaluates the iteration and returns the last element. This is a terminating operation. + * + * @return the last element after the iteration is evaluated + */ + Optional last(); + + /** + * Evaluates the iteration and leaves only the count last elements. + * + * @return the last counts elements as an Iterable + */ + FluentIterable last(int count); + + /** + * Transforms this FluentIterable into a new one containing objects of the type T. + * + * @param function a function that transforms an instance of E into an instance of T + * @param the target type of the transformation + * @return a new FluentIterable of the new type + */ + FluentIterable map(Function function); + + /** + * Returns the contents of this Iterable as a List. + * + * @return a List representation of this Iterable + */ + List asList(); + + /** + * Utility method that iterates over iterable and adds the contents to a list. + * + * @param iterable the iterable to collect + * @param the type of the objects to iterate + * @return a list with all objects of the given iterator + */ + static List copyToList(Iterable iterable) { + List copy = new ArrayList<>(); + Iterator iterator = iterable.iterator(); + while (iterator.hasNext()) { + copy.add(iterator.next()); + } + return copy; + } +} diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java new file mode 100644 index 000000000..07d7f5739 --- /dev/null +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java @@ -0,0 +1,78 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.fluentinterface.fluentiterable.lazy; + +import java.util.Iterator; + +/** + * This class is used to realize LazyFluentIterables. It decorates a given iterator. Does not + * support consecutive hasNext() calls. + */ +public abstract class DecoratingIterator implements Iterator { + + protected final Iterator fromIterator; + + private E next; + + /** + * Creates an iterator that decorates the given iterator. + */ + public DecoratingIterator(Iterator fromIterator) { + this.fromIterator = fromIterator; + } + + /** + * Precomputes and saves the next element of the Iterable. null is considered as end of data. + * + * @return true if a next element is available + */ + @Override + public final boolean hasNext() { + next = computeNext(); + return next != null; + } + + /** + * Returns the next element of the Iterable. + * + * @return the next element of the Iterable, or null if not present. + */ + @Override + public final E next() { + if (next == null) { + return fromIterator.next(); + } else { + final E result = next; + next = null; + return result; + } + } + + /** + * Computes the next object of the Iterable. Can be implemented to realize custom behaviour for an + * iteration process. null is considered as end of data. + * + * @return the next element of the Iterable. + */ + public abstract E computeNext(); +} diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java new file mode 100644 index 000000000..63247875c --- /dev/null +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java @@ -0,0 +1,249 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.fluentinterface.fluentiterable.lazy; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Predicate; + +import com.iluwatar.fluentinterface.fluentiterable.FluentIterable; + +/** + * This is a lazy implementation of the FluentIterable interface. It evaluates all chained + * operations when a terminating operation is applied. + * + * @param the type of the objects the iteration is about + */ +public class LazyFluentIterable implements FluentIterable { + + private final Iterable iterable; + + /** + * This constructor creates a new LazyFluentIterable. It wraps the given iterable. + * + * @param iterable the iterable this FluentIterable works on. + */ + protected LazyFluentIterable(Iterable iterable) { + this.iterable = iterable; + } + + /** + * This constructor can be used to implement anonymous subclasses of the LazyFluentIterable. + */ + protected LazyFluentIterable() { + iterable = this; + } + + /** + * Filters the contents of Iterable using the given predicate, leaving only the ones which satisfy + * the predicate. + * + * @param predicate the condition to test with for the filtering. If the test is negative, the + * tested object is removed by the iterator. + * @return a new FluentIterable object that decorates the source iterable + */ + @Override + public FluentIterable filter(Predicate predicate) { + return new LazyFluentIterable() { + @Override + public Iterator iterator() { + return new DecoratingIterator(iterable.iterator()) { + @Override + public E computeNext() { + while (fromIterator.hasNext()) { + E candidate = fromIterator.next(); + if (predicate.test(candidate)) { + return candidate; + } + } + + return null; + } + }; + } + }; + } + + /** + * Can be used to collect objects from the iteration. Is a terminating operation. + * + * @return an Optional containing the first object of this Iterable + */ + @Override + public Optional first() { + Iterator resultIterator = first(1).iterator(); + return resultIterator.hasNext() ? Optional.of(resultIterator.next()) : Optional.empty(); + } + + /** + * Can be used to collect objects from the iteration. + * + * @param count defines the number of objects to return + * @return the same FluentIterable with a collection decimated to a maximum of 'count' first + * objects. + */ + @Override + public FluentIterable first(int count) { + return new LazyFluentIterable() { + @Override + public Iterator iterator() { + return new DecoratingIterator(iterable.iterator()) { + int currentIndex; + + @Override + public E computeNext() { + if (currentIndex < count && fromIterator.hasNext()) { + E candidate = fromIterator.next(); + currentIndex++; + return candidate; + } + return null; + } + }; + } + }; + } + + /** + * Can be used to collect objects from the iteration. Is a terminating operation. + * + * @return an Optional containing the last object of this Iterable + */ + @Override + public Optional last() { + Iterator resultIterator = last(1).iterator(); + return resultIterator.hasNext() ? Optional.of(resultIterator.next()) : Optional.empty(); + } + + /** + * Can be used to collect objects from the Iterable. Is a terminating operation. This operation is + * memory intensive, because the contents of this Iterable are collected into a List, when the + * next object is requested. + * + * @param count defines the number of objects to return + * @return the same FluentIterable with a collection decimated to a maximum of 'count' last + * objects + */ + @Override + public FluentIterable last(int count) { + return new LazyFluentIterable() { + @Override + public Iterator iterator() { + return new DecoratingIterator(iterable.iterator()) { + private int stopIndex; + private int totalElementsCount; + private List list; + private int currentIndex; + + @Override + public E computeNext() { + initialize(); + + E candidate = null; + while (currentIndex < stopIndex && fromIterator.hasNext()) { + currentIndex++; + fromIterator.next(); + } + if (currentIndex >= stopIndex && fromIterator.hasNext()) { + candidate = fromIterator.next(); + } + return candidate; + } + + private void initialize() { + if (list == null) { + list = new ArrayList<>(); + Iterator newIterator = iterable.iterator(); + while (newIterator.hasNext()) { + list.add(newIterator.next()); + } + + totalElementsCount = list.size(); + stopIndex = totalElementsCount - count; + } + } + }; + } + }; + } + + /** + * Transforms this FluentIterable into a new one containing objects of the type T. + * + * @param function a function that transforms an instance of E into an instance of T + * @param the target type of the transformation + * @return a new FluentIterable of the new type + */ + @Override + public FluentIterable map(Function function) { + return new LazyFluentIterable() { + @Override + public Iterator iterator() { + return new DecoratingIterator(null) { + Iterator oldTypeIterator = iterable.iterator(); + + @Override + public T computeNext() { + if (oldTypeIterator.hasNext()) { + E candidate = oldTypeIterator.next(); + return function.apply(candidate); + } else { + return null; + } + } + }; + } + }; + } + + /** + * Collects all remaining objects of this iteration into a list. + * + * @return a list with all remaining objects of this iteration + */ + @Override + public List asList() { + return FluentIterable.copyToList(iterable); + } + + @Override + public Iterator iterator() { + return new DecoratingIterator(iterable.iterator()) { + @Override + public E computeNext() { + return fromIterator.hasNext() ? fromIterator.next() : null; + } + }; + } + + /** + * @return a FluentIterable from a given iterable. Calls the LazyFluentIterable constructor. + */ + public static final FluentIterable from(Iterable iterable) { + return new LazyFluentIterable<>(iterable); + } + +} diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java new file mode 100644 index 000000000..153f3faa5 --- /dev/null +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java @@ -0,0 +1,224 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.fluentinterface.fluentiterable.simple; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.Spliterator; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; + +import com.iluwatar.fluentinterface.fluentiterable.FluentIterable; + +/** + * This is a simple implementation of the FluentIterable interface. It evaluates all chained + * operations eagerly. This implementation would be costly to be utilized in real applications. + * + * @param the type of the objects the iteration is about + */ +public class SimpleFluentIterable implements FluentIterable { + + private final Iterable iterable; + + /** + * This constructor creates a copy of a given iterable's contents. + * + * @param iterable the iterable this interface copies to work on. + */ + protected SimpleFluentIterable(Iterable iterable) { + this.iterable = iterable; + } + + /** + * Filters the contents of Iterable using the given predicate, leaving only the ones which satisfy + * the predicate. + * + * @param predicate the condition to test with for the filtering. If the test is negative, the + * tested object is removed by the iterator. + * @return the same FluentIterable with a filtered collection + */ + @Override + public final FluentIterable filter(Predicate predicate) { + Iterator iterator = iterator(); + while (iterator.hasNext()) { + E nextElement = iterator.next(); + if (!predicate.test(nextElement)) { + iterator.remove(); + } + } + return this; + } + + /** + * Can be used to collect objects from the Iterable. Is a terminating operation. + * + * @return an option of the first object of the Iterable + */ + @Override + public final Optional first() { + Iterator resultIterator = first(1).iterator(); + return resultIterator.hasNext() ? Optional.of(resultIterator.next()) : Optional.empty(); + } + + /** + * Can be used to collect objects from the Iterable. Is a terminating operation. + * + * @param count defines the number of objects to return + * @return the same FluentIterable with a collection decimated to a maximum of 'count' first + * objects. + */ + @Override + public final FluentIterable first(int count) { + Iterator iterator = iterator(); + int currentCount = 0; + while (iterator.hasNext()) { + iterator.next(); + if (currentCount >= count) { + iterator.remove(); + } + currentCount++; + } + return this; + } + + /** + * Can be used to collect objects from the Iterable. Is a terminating operation. + * + * @return an option of the last object of the Iterable + */ + @Override + public final Optional last() { + List list = last(1).asList(); + if (list.isEmpty()) { + return Optional.empty(); + } + return Optional.of(list.get(0)); + } + + /** + * Can be used to collect objects from the Iterable. Is a terminating operation. + * + * @param count defines the number of objects to return + * @return the same FluentIterable with a collection decimated to a maximum of 'count' last + * objects + */ + @Override + public final FluentIterable last(int count) { + int remainingElementsCount = getRemainingElementsCount(); + Iterator iterator = iterator(); + int currentIndex = 0; + while (iterator.hasNext()) { + iterator.next(); + if (currentIndex < remainingElementsCount - count) { + iterator.remove(); + } + currentIndex++; + } + + return this; + } + + /** + * Transforms this FluentIterable into a new one containing objects of the type T. + * + * @param function a function that transforms an instance of E into an instance of T + * @param the target type of the transformation + * @return a new FluentIterable of the new type + */ + @Override + public final FluentIterable map(Function function) { + List temporaryList = new ArrayList<>(); + Iterator iterator = iterator(); + while (iterator.hasNext()) { + temporaryList.add(function.apply(iterator.next())); + } + return from(temporaryList); + } + + /** + * Collects all remaining objects of this Iterable into a list. + * + * @return a list with all remaining objects of this Iterable + */ + @Override + public List asList() { + return toList(iterable.iterator()); + } + + /** + * @return a FluentIterable from a given iterable. Calls the SimpleFluentIterable constructor. + */ + public static final FluentIterable from(Iterable iterable) { + return new SimpleFluentIterable<>(iterable); + } + + public static final FluentIterable fromCopyOf(Iterable iterable) { + List copy = FluentIterable.copyToList(iterable); + return new SimpleFluentIterable<>(copy); + } + + @Override + public Iterator iterator() { + return iterable.iterator(); + } + + @Override + public void forEach(Consumer action) { + iterable.forEach(action); + } + + + @Override + public Spliterator spliterator() { + return iterable.spliterator(); + } + + /** + * @return the count of remaining objects of the current Iterable + */ + public final int getRemainingElementsCount() { + int counter = 0; + Iterator iterator = iterator(); + while (iterator.hasNext()) { + iterator.next(); + counter++; + } + return counter; + } + + /** + * Collects the remaining objects of the given iterator into a List. + * + * @return a new List with the remaining objects. + */ + public static List toList(Iterator iterator) { + List copy = new ArrayList<>(); + while (iterator.hasNext()) { + copy.add(iterator.next()); + } + return copy; + } +} diff --git a/fluentinterface/src/test/java/com/iluwatar/fluentinterface/app/AppTest.java b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/app/AppTest.java new file mode 100644 index 000000000..5b750c604 --- /dev/null +++ b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/app/AppTest.java @@ -0,0 +1,34 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.fluentinterface.app; + +import org.junit.Test; + +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/fluentinterface/src/test/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterableTest.java b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterableTest.java new file mode 100644 index 000000000..4037bed49 --- /dev/null +++ b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterableTest.java @@ -0,0 +1,192 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.fluentinterface.fluentiterable; + +import org.junit.Test; + +import java.util.*; +import java.util.function.Consumer; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +/** + * Date: 12/12/15 - 7:00 PM + * + * @author Jeroen Meulemeester + */ +public abstract class FluentIterableTest { + + /** + * Create a new {@link FluentIterable} from the given integers + * + * @param integers The integers + * @return The new iterable, use for testing + */ + protected abstract FluentIterable createFluentIterable(final Iterable integers); + + @Test + public void testFirst() throws Exception { + final List integers = Arrays.asList(1, 2, 3, 10, 9, 8); + final Optional first = createFluentIterable(integers).first(); + assertNotNull(first); + assertTrue(first.isPresent()); + assertEquals(integers.get(0), first.get()); + } + + @Test + public void testFirstEmptyCollection() throws Exception { + final List integers = Collections.emptyList(); + final Optional first = createFluentIterable(integers).first(); + assertNotNull(first); + assertFalse(first.isPresent()); + } + + @Test + public void testFirstCount() throws Exception { + final List integers = Arrays.asList(1, 2, 3, 10, 9, 8); + final List first4 = createFluentIterable(integers) + .first(4) + .asList(); + + assertNotNull(first4); + assertEquals(4, first4.size()); + + assertEquals(integers.get(0), first4.get(0)); + assertEquals(integers.get(1), first4.get(1)); + assertEquals(integers.get(2), first4.get(2)); + assertEquals(integers.get(3), first4.get(3)); + } + + @Test + public void testFirstCountLessItems() throws Exception { + final List integers = Arrays.asList(1, 2, 3); + final List first4 = createFluentIterable(integers) + .first(4) + .asList(); + + assertNotNull(first4); + assertEquals(3, first4.size()); + + assertEquals(integers.get(0), first4.get(0)); + assertEquals(integers.get(1), first4.get(1)); + assertEquals(integers.get(2), first4.get(2)); + } + + @Test + public void testLast() throws Exception { + final List integers = Arrays.asList(1, 2, 3, 10, 9, 8); + final Optional last = createFluentIterable(integers).last(); + assertNotNull(last); + assertTrue(last.isPresent()); + assertEquals(integers.get(integers.size() - 1), last.get()); + } + + @Test + public void testLastEmptyCollection() throws Exception { + final List integers = Collections.emptyList(); + final Optional last = createFluentIterable(integers).last(); + assertNotNull(last); + assertFalse(last.isPresent()); + } + + @Test + public void testLastCount() throws Exception { + final List integers = Arrays.asList(1, 2, 3, 10, 9, 8); + final List last4 = createFluentIterable(integers) + .last(4) + .asList(); + + assertNotNull(last4); + assertEquals(4, last4.size()); + assertEquals(Integer.valueOf(3), last4.get(0)); + assertEquals(Integer.valueOf(10), last4.get(1)); + assertEquals(Integer.valueOf(9), last4.get(2)); + assertEquals(Integer.valueOf(8), last4.get(3)); + } + + @Test + public void testLastCountLessItems() throws Exception { + final List integers = Arrays.asList(1, 2, 3); + final List last4 = createFluentIterable(integers) + .last(4) + .asList(); + + assertNotNull(last4); + assertEquals(3, last4.size()); + + assertEquals(Integer.valueOf(1), last4.get(0)); + assertEquals(Integer.valueOf(2), last4.get(1)); + assertEquals(Integer.valueOf(3), last4.get(2)); + } + + @Test + public void testFilter() throws Exception { + final List integers = Arrays.asList(1, 2, 3, 10, 9, 8); + final List evenItems = createFluentIterable(integers) + .filter(i -> i % 2 == 0) + .asList(); + + assertNotNull(evenItems); + assertEquals(3, evenItems.size()); + assertEquals(Integer.valueOf(2), evenItems.get(0)); + assertEquals(Integer.valueOf(10), evenItems.get(1)); + assertEquals(Integer.valueOf(8), evenItems.get(2)); + } + + @Test + public void testMap() throws Exception { + final List integers = Arrays.asList(1, 2, 3); + final List longs = createFluentIterable(integers) + .map(Integer::longValue) + .asList(); + + assertNotNull(longs); + assertEquals(integers.size(), longs.size()); + assertEquals(Long.valueOf(1), longs.get(0)); + assertEquals(Long.valueOf(2), longs.get(1)); + assertEquals(Long.valueOf(3), longs.get(2)); + } + + @Test + public void testForEach() throws Exception { + final List integers = Arrays.asList(1, 2, 3); + + final Consumer consumer = mock(Consumer.class); + createFluentIterable(integers).forEach(consumer); + + verify(consumer, times(1)).accept(Integer.valueOf(1)); + verify(consumer, times(1)).accept(Integer.valueOf(2)); + verify(consumer, times(1)).accept(Integer.valueOf(3)); + verifyNoMoreInteractions(consumer); + + } + + @Test + public void testSpliterator() throws Exception { + final List integers = Arrays.asList(1, 2, 3); + final Spliterator split = createFluentIterable(integers).spliterator(); + assertNotNull(split); + } + +} \ No newline at end of file diff --git a/fluentinterface/src/test/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterableTest.java b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterableTest.java new file mode 100644 index 000000000..c422903c8 --- /dev/null +++ b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterableTest.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.fluentinterface.fluentiterable.lazy; + +import com.iluwatar.fluentinterface.fluentiterable.FluentIterable; +import com.iluwatar.fluentinterface.fluentiterable.FluentIterableTest; + +/** + * Date: 12/12/15 - 7:56 PM + * + * @author Jeroen Meulemeester + */ +public class LazyFluentIterableTest extends FluentIterableTest { + + @Override + protected FluentIterable createFluentIterable(Iterable integers) { + return LazyFluentIterable.from(integers); + } + +} diff --git a/fluentinterface/src/test/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterableTest.java b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterableTest.java new file mode 100644 index 000000000..e4e80641c --- /dev/null +++ b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterableTest.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.fluentinterface.fluentiterable.simple; + +import com.iluwatar.fluentinterface.fluentiterable.FluentIterable; +import com.iluwatar.fluentinterface.fluentiterable.FluentIterableTest; + +/** + * Date: 12/12/15 - 7:56 PM + * + * @author Jeroen Meulemeester + */ +public class SimpleFluentIterableTest extends FluentIterableTest { + + @Override + protected FluentIterable createFluentIterable(Iterable integers) { + return SimpleFluentIterable.fromCopyOf(integers); + } + +} diff --git a/flux/README.md b/flux/README.md new file mode 100644 index 000000000..7ac312c44 --- /dev/null +++ b/flux/README.md @@ -0,0 +1,27 @@ +--- +layout: pattern +title: Flux +folder: flux +permalink: /patterns/flux/ +categories: Presentation Tier +tags: + - Java + - Difficulty-Intermediate +--- + +## Intent +Flux eschews MVC in favor of a unidirectional data flow. When a +user interacts with a view, the view propagates an action through a central +dispatcher, to the various stores that hold the application's data and business +logic, which updates all of the views that are affected. + +![alt text](./etc/flux.png "Flux") + +## Applicability +Use the Flux pattern when + +* you want to focus on creating explicit and understandable update paths for your application's data, which makes tracing changes during development simpler and makes bugs easier to track down and fix. + +## Credits + +* [Flux - Application architecture for building user interfaces](http://facebook.github.io/flux/) diff --git a/flux/pom.xml b/flux/pom.xml index ad511a53c..c07cca157 100644 --- a/flux/pom.xml +++ b/flux/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT flux @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/flux/src/main/java/com/iluwatar/flux/action/Action.java b/flux/src/main/java/com/iluwatar/flux/action/Action.java index 24d294be7..7fc0fe51a 100644 --- a/flux/src/main/java/com/iluwatar/flux/action/Action.java +++ b/flux/src/main/java/com/iluwatar/flux/action/Action.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.flux.action; /** @@ -7,13 +29,13 @@ package com.iluwatar.flux.action; */ public abstract class Action { - private ActionType type; - - public Action(ActionType type) { - this.type = type; - } - - public ActionType getType() { - return type; - } + private ActionType type; + + public Action(ActionType type) { + this.type = type; + } + + public ActionType getType() { + return type; + } } diff --git a/flux/src/main/java/com/iluwatar/flux/action/ActionType.java b/flux/src/main/java/com/iluwatar/flux/action/ActionType.java index 318ca1b12..db8046d5e 100644 --- a/flux/src/main/java/com/iluwatar/flux/action/ActionType.java +++ b/flux/src/main/java/com/iluwatar/flux/action/ActionType.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.flux.action; /** @@ -7,6 +29,6 @@ package com.iluwatar.flux.action; */ public enum ActionType { - MENU_ITEM_SELECTED, CONTENT_CHANGED; - + MENU_ITEM_SELECTED, CONTENT_CHANGED; + } diff --git a/flux/src/main/java/com/iluwatar/flux/action/Content.java b/flux/src/main/java/com/iluwatar/flux/action/Content.java index e53871d89..49a06b5ad 100644 --- a/flux/src/main/java/com/iluwatar/flux/action/Content.java +++ b/flux/src/main/java/com/iluwatar/flux/action/Content.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.flux.action; /** @@ -6,17 +28,18 @@ package com.iluwatar.flux.action; * */ public enum Content { - - PRODUCTS("Products - This page lists the company's products."), COMPANY("Company - This page displays information about the company."); - - private String title; - private Content(String title) { - this.title = title; - } - - @Override - public String toString() { - return title; - } + PRODUCTS("Products - This page lists the company's products."), COMPANY( + "Company - This page displays information about the company."); + + private String title; + + private Content(String title) { + this.title = title; + } + + @Override + public String toString() { + return title; + } } diff --git a/flux/src/main/java/com/iluwatar/flux/action/ContentAction.java b/flux/src/main/java/com/iluwatar/flux/action/ContentAction.java index 2c1a40e6e..c066ba933 100644 --- a/flux/src/main/java/com/iluwatar/flux/action/ContentAction.java +++ b/flux/src/main/java/com/iluwatar/flux/action/ContentAction.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.flux.action; /** @@ -7,14 +29,14 @@ package com.iluwatar.flux.action; */ public class ContentAction extends Action { - private Content content; + private Content content; - public ContentAction(Content content) { - super(ActionType.CONTENT_CHANGED); - this.content = content; - } - - public Content getContent() { - return content; - } + public ContentAction(Content content) { + super(ActionType.CONTENT_CHANGED); + this.content = content; + } + + public Content getContent() { + return content; + } } diff --git a/flux/src/main/java/com/iluwatar/flux/action/MenuAction.java b/flux/src/main/java/com/iluwatar/flux/action/MenuAction.java index a3dd9875e..09c720503 100644 --- a/flux/src/main/java/com/iluwatar/flux/action/MenuAction.java +++ b/flux/src/main/java/com/iluwatar/flux/action/MenuAction.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.flux.action; @@ -8,14 +30,14 @@ package com.iluwatar.flux.action; */ public class MenuAction extends Action { - private MenuItem menuItem; + private MenuItem menuItem; - public MenuAction(MenuItem menuItem) { - super(ActionType.MENU_ITEM_SELECTED); - this.menuItem = menuItem; - } - - public MenuItem getMenuItem() { - return menuItem; - } + public MenuAction(MenuItem menuItem) { + super(ActionType.MENU_ITEM_SELECTED); + this.menuItem = menuItem; + } + + public MenuItem getMenuItem() { + return menuItem; + } } diff --git a/flux/src/main/java/com/iluwatar/flux/action/MenuItem.java b/flux/src/main/java/com/iluwatar/flux/action/MenuItem.java index d842fca78..ce8c5ed64 100644 --- a/flux/src/main/java/com/iluwatar/flux/action/MenuItem.java +++ b/flux/src/main/java/com/iluwatar/flux/action/MenuItem.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.flux.action; /** @@ -6,17 +28,17 @@ package com.iluwatar.flux.action; * */ public enum MenuItem { - - HOME("Home"), PRODUCTS("Products"), COMPANY("Company"); - - private String title; - MenuItem(String title) { - this.title = title; - } - - @Override - public String toString() { - return title; - } + HOME("Home"), PRODUCTS("Products"), COMPANY("Company"); + + private String title; + + MenuItem(String title) { + this.title = title; + } + + @Override + public String toString() { + return title; + } } diff --git a/flux/src/main/java/com/iluwatar/flux/app/App.java b/flux/src/main/java/com/iluwatar/flux/app/App.java index d0c7763e7..81aac980c 100644 --- a/flux/src/main/java/com/iluwatar/flux/app/App.java +++ b/flux/src/main/java/com/iluwatar/flux/app/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.flux.app; import com.iluwatar.flux.action.MenuItem; @@ -9,38 +31,45 @@ import com.iluwatar.flux.view.MenuView; /** * - * Flux is the application architecture that Facebook uses for building client-side web - * applications. Flux eschews MVC in favor of a unidirectional data flow. When a user interacts with - * a React view, the view propagates an action through a central dispatcher, to the various stores that - * hold the application's data and business logic, which updates all of the views that are affected. - * - * This example has two views: menu and content. They represent typical main menu and content area of - * a web page. When menu item is clicked it triggers events through the dispatcher. The events are - * received and handled by the stores updating their data as needed. The stores then notify the views - * that they should rerender themselves. - * + * Flux is the application architecture that Facebook uses for building client-side web + * applications. Flux eschews MVC in favor of a unidirectional data flow. When a user interacts with + * a React view, the view propagates an action through a central dispatcher, to the various stores + * that hold the application's data and business logic, which updates all of the views that are + * affected. + *

+ * This example has two views: menu and content. They represent typical main menu and content area + * of a web page. When menu item is clicked it triggers events through the dispatcher. The events + * are received and handled by the stores updating their data as needed. The stores then notify the + * views that they should rerender themselves. + *

* http://facebook.github.io/flux/docs/overview.html * */ public class App { - - public static void main( String[] args ) { - // initialize and wire the system - MenuStore menuStore = new MenuStore(); - Dispatcher.getInstance().registerStore(menuStore); - ContentStore contentStore = new ContentStore(); - Dispatcher.getInstance().registerStore(contentStore); - MenuView menuView = new MenuView(); - menuStore.registerView(menuView); - ContentView contentView = new ContentView(); - contentStore.registerView(contentView); - - // render initial view - menuView.render(); - contentView.render(); - - // user clicks another menu item - // this triggers action dispatching and eventually causes views to render with new content - menuView.itemClicked(MenuItem.COMPANY); - } + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + + // initialize and wire the system + MenuStore menuStore = new MenuStore(); + Dispatcher.getInstance().registerStore(menuStore); + ContentStore contentStore = new ContentStore(); + Dispatcher.getInstance().registerStore(contentStore); + MenuView menuView = new MenuView(); + menuStore.registerView(menuView); + ContentView contentView = new ContentView(); + contentStore.registerView(contentView); + + // render initial view + menuView.render(); + contentView.render(); + + // user clicks another menu item + // this triggers action dispatching and eventually causes views to render with new content + menuView.itemClicked(MenuItem.COMPANY); + } } diff --git a/flux/src/main/java/com/iluwatar/flux/dispatcher/Dispatcher.java b/flux/src/main/java/com/iluwatar/flux/dispatcher/Dispatcher.java index 8bf03e4b0..10f064efa 100644 --- a/flux/src/main/java/com/iluwatar/flux/dispatcher/Dispatcher.java +++ b/flux/src/main/java/com/iluwatar/flux/dispatcher/Dispatcher.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.flux.dispatcher; import java.util.LinkedList; @@ -15,38 +37,40 @@ import com.iluwatar.flux.store.Store; * Dispatcher sends Actions to registered Stores. * */ -public class Dispatcher { - - private static Dispatcher instance = new Dispatcher(); - - private List stores = new LinkedList<>(); - - private Dispatcher() { - } +public final class Dispatcher { - public static Dispatcher getInstance() { - return instance; - } - - public void registerStore(Store store) { - stores.add(store); - } - - public void menuItemSelected(MenuItem menuItem) { - dispatchAction(new MenuAction(menuItem)); - switch (menuItem) { - case HOME: - case PRODUCTS: - default: - dispatchAction(new ContentAction(Content.PRODUCTS)); - break; - case COMPANY: - dispatchAction(new ContentAction(Content.COMPANY)); - break; - } - } - - private void dispatchAction(Action action) { - stores.stream().forEach((store) -> store.onAction(action)); - } + private static Dispatcher instance = new Dispatcher(); + + private List stores = new LinkedList<>(); + + private Dispatcher() {} + + public static Dispatcher getInstance() { + return instance; + } + + public void registerStore(Store store) { + stores.add(store); + } + + /** + * Menu item selected handler + */ + public void menuItemSelected(MenuItem menuItem) { + dispatchAction(new MenuAction(menuItem)); + switch (menuItem) { + case HOME: + case PRODUCTS: + default: + dispatchAction(new ContentAction(Content.PRODUCTS)); + break; + case COMPANY: + dispatchAction(new ContentAction(Content.COMPANY)); + break; + } + } + + private void dispatchAction(Action action) { + stores.stream().forEach(store -> store.onAction(action)); + } } diff --git a/flux/src/main/java/com/iluwatar/flux/store/ContentStore.java b/flux/src/main/java/com/iluwatar/flux/store/ContentStore.java index 15d031abd..bca8e29bf 100644 --- a/flux/src/main/java/com/iluwatar/flux/store/ContentStore.java +++ b/flux/src/main/java/com/iluwatar/flux/store/ContentStore.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.flux.store; import com.iluwatar.flux.action.Action; @@ -12,18 +34,18 @@ import com.iluwatar.flux.action.ContentAction; */ public class ContentStore extends Store { - private Content content = Content.PRODUCTS; + private Content content = Content.PRODUCTS; - @Override - public void onAction(Action action) { - if (action.getType().equals(ActionType.CONTENT_CHANGED)) { - ContentAction contentAction = (ContentAction) action; - content = contentAction.getContent(); - notifyChange(); - } - } - - public Content getContent() { - return content; - } + @Override + public void onAction(Action action) { + if (action.getType().equals(ActionType.CONTENT_CHANGED)) { + ContentAction contentAction = (ContentAction) action; + content = contentAction.getContent(); + notifyChange(); + } + } + + public Content getContent() { + return content; + } } diff --git a/flux/src/main/java/com/iluwatar/flux/store/MenuStore.java b/flux/src/main/java/com/iluwatar/flux/store/MenuStore.java index 3e614ac73..2b3b418b3 100644 --- a/flux/src/main/java/com/iluwatar/flux/store/MenuStore.java +++ b/flux/src/main/java/com/iluwatar/flux/store/MenuStore.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.flux.store; import com.iluwatar.flux.action.Action; @@ -12,18 +34,18 @@ import com.iluwatar.flux.action.MenuItem; */ public class MenuStore extends Store { - private MenuItem selected = MenuItem.HOME; - - @Override - public void onAction(Action action) { - if (action.getType().equals(ActionType.MENU_ITEM_SELECTED)) { - MenuAction menuAction = (MenuAction) action; - selected = menuAction.getMenuItem(); - notifyChange(); - } - } - - public MenuItem getSelected() { - return selected; - } + private MenuItem selected = MenuItem.HOME; + + @Override + public void onAction(Action action) { + if (action.getType().equals(ActionType.MENU_ITEM_SELECTED)) { + MenuAction menuAction = (MenuAction) action; + selected = menuAction.getMenuItem(); + notifyChange(); + } + } + + public MenuItem getSelected() { + return selected; + } } diff --git a/flux/src/main/java/com/iluwatar/flux/store/Store.java b/flux/src/main/java/com/iluwatar/flux/store/Store.java index 326af404b..f7cc2ccbf 100644 --- a/flux/src/main/java/com/iluwatar/flux/store/Store.java +++ b/flux/src/main/java/com/iluwatar/flux/store/Store.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.flux.store; import java.util.LinkedList; @@ -12,16 +34,16 @@ import com.iluwatar.flux.view.View; * */ public abstract class Store { - - private List views = new LinkedList<>(); - - public abstract void onAction(Action action); - public void registerView(View view) { - views.add(view); - } - - protected void notifyChange() { - views.stream().forEach((view) -> view.storeChanged(this)); - } + private List views = new LinkedList<>(); + + public abstract void onAction(Action action); + + public void registerView(View view) { + views.add(view); + } + + protected void notifyChange() { + views.stream().forEach(view -> view.storeChanged(this)); + } } diff --git a/flux/src/main/java/com/iluwatar/flux/view/ContentView.java b/flux/src/main/java/com/iluwatar/flux/view/ContentView.java index b12c8b7a2..cb351bfae 100644 --- a/flux/src/main/java/com/iluwatar/flux/view/ContentView.java +++ b/flux/src/main/java/com/iluwatar/flux/view/ContentView.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.flux.view; import com.iluwatar.flux.action.Content; @@ -11,17 +33,17 @@ import com.iluwatar.flux.store.Store; */ public class ContentView implements View { - private Content content = Content.PRODUCTS; + private Content content = Content.PRODUCTS; - @Override - public void storeChanged(Store store) { - ContentStore contentStore = (ContentStore) store; - content = contentStore.getContent(); - render(); - } + @Override + public void storeChanged(Store store) { + ContentStore contentStore = (ContentStore) store; + content = contentStore.getContent(); + render(); + } - @Override - public void render() { - System.out.println(content.toString()); - } + @Override + public void render() { + System.out.println(content.toString()); + } } diff --git a/flux/src/main/java/com/iluwatar/flux/view/MenuView.java b/flux/src/main/java/com/iluwatar/flux/view/MenuView.java index f37d21bb9..6cd9005dc 100644 --- a/flux/src/main/java/com/iluwatar/flux/view/MenuView.java +++ b/flux/src/main/java/com/iluwatar/flux/view/MenuView.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.flux.view; import com.iluwatar.flux.action.MenuItem; @@ -12,27 +34,27 @@ import com.iluwatar.flux.store.Store; */ public class MenuView implements View { - private MenuItem selected = MenuItem.HOME; - - @Override - public void storeChanged(Store store) { - MenuStore menuStore = (MenuStore) store; - selected = menuStore.getSelected(); - render(); - } + private MenuItem selected = MenuItem.HOME; - @Override - public void render() { - for (MenuItem item: MenuItem.values()) { - if (selected.equals(item)) { - System.out.println(String.format("* %s", item.toString())); - } else { - System.out.println(item.toString()); - } - } - } - - public void itemClicked(MenuItem item) { - Dispatcher.getInstance().menuItemSelected(item); - } + @Override + public void storeChanged(Store store) { + MenuStore menuStore = (MenuStore) store; + selected = menuStore.getSelected(); + render(); + } + + @Override + public void render() { + for (MenuItem item : MenuItem.values()) { + if (selected.equals(item)) { + System.out.println(String.format("* %s", item.toString())); + } else { + System.out.println(item.toString()); + } + } + } + + public void itemClicked(MenuItem item) { + Dispatcher.getInstance().menuItemSelected(item); + } } diff --git a/flux/src/main/java/com/iluwatar/flux/view/View.java b/flux/src/main/java/com/iluwatar/flux/view/View.java index 4eb6ee3fb..0a34f1fdf 100644 --- a/flux/src/main/java/com/iluwatar/flux/view/View.java +++ b/flux/src/main/java/com/iluwatar/flux/view/View.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.flux.view; import com.iluwatar.flux.store.Store; @@ -9,7 +31,7 @@ import com.iluwatar.flux.store.Store; */ public interface View { - public void storeChanged(Store store); + void storeChanged(Store store); - public void render(); + void render(); } diff --git a/flux/src/test/java/com/iluwatar/flux/action/ContentTest.java b/flux/src/test/java/com/iluwatar/flux/action/ContentTest.java new file mode 100644 index 000000000..90e66d69a --- /dev/null +++ b/flux/src/test/java/com/iluwatar/flux/action/ContentTest.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.flux.action; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/12/15 - 10:11 PM + * + * @author Jeroen Meulemeester + */ +public class ContentTest { + + @Test + public void testToString() throws Exception { + for (final Content content : Content.values()) { + final String toString = content.toString(); + assertNotNull(toString); + assertFalse(toString.trim().isEmpty()); + } + } + +} diff --git a/flux/src/test/java/com/iluwatar/flux/action/MenuItemTest.java b/flux/src/test/java/com/iluwatar/flux/action/MenuItemTest.java new file mode 100644 index 000000000..02c8e972b --- /dev/null +++ b/flux/src/test/java/com/iluwatar/flux/action/MenuItemTest.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.flux.action; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/12/15 - 10:15 PM + * + * @author Jeroen Meulemeester + */ +public class MenuItemTest { + + @Test + public void testToString() throws Exception { + for (final MenuItem menuItem : MenuItem.values()) { + final String toString = menuItem.toString(); + assertNotNull(toString); + assertFalse(toString.trim().isEmpty()); + } + } + +} diff --git a/flux/src/test/java/com/iluwatar/flux/app/AppTest.java b/flux/src/test/java/com/iluwatar/flux/app/AppTest.java index 902e5872e..f25beaefa 100644 --- a/flux/src/test/java/com/iluwatar/flux/app/AppTest.java +++ b/flux/src/test/java/com/iluwatar/flux/app/AppTest.java @@ -1,14 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.flux.app; import org.junit.Test; -import com.iluwatar.flux.app.App; - +/** + * + * Application test + * + */ public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } + + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/flux/src/test/java/com/iluwatar/flux/dispatcher/DispatcherTest.java b/flux/src/test/java/com/iluwatar/flux/dispatcher/DispatcherTest.java new file mode 100644 index 000000000..9f3f610d1 --- /dev/null +++ b/flux/src/test/java/com/iluwatar/flux/dispatcher/DispatcherTest.java @@ -0,0 +1,113 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.flux.dispatcher; + +import com.iluwatar.flux.action.Action; +import com.iluwatar.flux.action.ActionType; +import com.iluwatar.flux.action.Content; +import com.iluwatar.flux.action.ContentAction; +import com.iluwatar.flux.action.MenuAction; +import com.iluwatar.flux.action.MenuItem; +import com.iluwatar.flux.store.Store; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.util.List; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/12/15 - 8:22 PM + * + * @author Jeroen Meulemeester + */ +public class DispatcherTest { + + /** + * Dispatcher is a singleton with no way to reset it's internal state back to the beginning. + * Replace the instance with a fresh one before each test to make sure test cases have no + * influence on each other. + */ + @Before + public void setUp() throws Exception { + final Constructor constructor; + constructor = Dispatcher.class.getDeclaredConstructor(); + constructor.setAccessible(true); + + final Field field = Dispatcher.class.getDeclaredField("instance"); + field.setAccessible(true); + field.set(Dispatcher.getInstance(), constructor.newInstance()); + } + + @Test + public void testGetInstance() throws Exception { + assertNotNull(Dispatcher.getInstance()); + assertSame(Dispatcher.getInstance(), Dispatcher.getInstance()); + } + + @Test + public void testMenuItemSelected() throws Exception { + final Dispatcher dispatcher = Dispatcher.getInstance(); + + final Store store = mock(Store.class); + dispatcher.registerStore(store); + dispatcher.menuItemSelected(MenuItem.HOME); + dispatcher.menuItemSelected(MenuItem.COMPANY); + + // We expect 4 events, 2 menu selections and 2 content change actions + final ArgumentCaptor actionCaptor = ArgumentCaptor.forClass(Action.class); + verify(store, times(4)).onAction(actionCaptor.capture()); + verifyNoMoreInteractions(store); + + final List actions = actionCaptor.getAllValues(); + final List menuActions = actions.stream() + .filter(a -> a.getType().equals(ActionType.MENU_ITEM_SELECTED)) + .map(a -> (MenuAction) a) + .collect(Collectors.toList()); + + final List contentActions = actions.stream() + .filter(a -> a.getType().equals(ActionType.CONTENT_CHANGED)) + .map(a -> (ContentAction) a) + .collect(Collectors.toList()); + + assertEquals(2, menuActions.size()); + assertEquals(1, menuActions.stream().map(MenuAction::getMenuItem).filter(MenuItem.HOME::equals).count()); + assertEquals(1, menuActions.stream().map(MenuAction::getMenuItem).filter(MenuItem.COMPANY::equals).count()); + + assertEquals(2, contentActions.size()); + assertEquals(1, contentActions.stream().map(ContentAction::getContent).filter(Content.PRODUCTS::equals).count()); + assertEquals(1, contentActions.stream().map(ContentAction::getContent).filter(Content.COMPANY::equals).count()); + + } + +} diff --git a/flux/src/test/java/com/iluwatar/flux/store/ContentStoreTest.java b/flux/src/test/java/com/iluwatar/flux/store/ContentStoreTest.java new file mode 100644 index 000000000..6e2cc5b35 --- /dev/null +++ b/flux/src/test/java/com/iluwatar/flux/store/ContentStoreTest.java @@ -0,0 +1,68 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.flux.store; + +import com.iluwatar.flux.action.Content; +import com.iluwatar.flux.action.ContentAction; +import com.iluwatar.flux.action.MenuAction; +import com.iluwatar.flux.action.MenuItem; +import com.iluwatar.flux.view.View; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/12/15 - 10:18 PM + * + * @author Jeroen Meulemeester + */ +public class ContentStoreTest { + + @Test + public void testOnAction() throws Exception { + final ContentStore contentStore = new ContentStore(); + + final View view = mock(View.class); + contentStore.registerView(view); + + verifyZeroInteractions(view); + + // Content should not react on menu action ... + contentStore.onAction(new MenuAction(MenuItem.PRODUCTS)); + verifyZeroInteractions(view); + + // ... but it should react on a content action + contentStore.onAction(new ContentAction(Content.COMPANY)); + verify(view, times(1)).storeChanged(eq(contentStore)); + verifyNoMoreInteractions(view); + assertEquals(Content.COMPANY, contentStore.getContent()); + + } + +} diff --git a/flux/src/test/java/com/iluwatar/flux/store/MenuStoreTest.java b/flux/src/test/java/com/iluwatar/flux/store/MenuStoreTest.java new file mode 100644 index 000000000..7d68656a2 --- /dev/null +++ b/flux/src/test/java/com/iluwatar/flux/store/MenuStoreTest.java @@ -0,0 +1,68 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.flux.store; + +import com.iluwatar.flux.action.Content; +import com.iluwatar.flux.action.ContentAction; +import com.iluwatar.flux.action.MenuAction; +import com.iluwatar.flux.action.MenuItem; +import com.iluwatar.flux.view.View; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/12/15 - 10:18 PM + * + * @author Jeroen Meulemeester + */ +public class MenuStoreTest { + + @Test + public void testOnAction() throws Exception { + final MenuStore menuStore = new MenuStore(); + + final View view = mock(View.class); + menuStore.registerView(view); + + verifyZeroInteractions(view); + + // Menu should not react on content action ... + menuStore.onAction(new ContentAction(Content.COMPANY)); + verifyZeroInteractions(view); + + // ... but it should react on a menu action + menuStore.onAction(new MenuAction(MenuItem.PRODUCTS)); + verify(view, times(1)).storeChanged(eq(menuStore)); + verifyNoMoreInteractions(view); + assertEquals(MenuItem.PRODUCTS, menuStore.getSelected()); + + } + +} diff --git a/flux/src/test/java/com/iluwatar/flux/view/ContentViewTest.java b/flux/src/test/java/com/iluwatar/flux/view/ContentViewTest.java new file mode 100644 index 000000000..cb26f97d6 --- /dev/null +++ b/flux/src/test/java/com/iluwatar/flux/view/ContentViewTest.java @@ -0,0 +1,54 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.flux.view; + +import com.iluwatar.flux.action.Content; +import com.iluwatar.flux.store.ContentStore; +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/12/15 - 10:31 PM + * + * @author Jeroen Meulemeester + */ +public class ContentViewTest { + + @Test + public void testStoreChanged() throws Exception { + final ContentStore store = mock(ContentStore.class); + when(store.getContent()).thenReturn(Content.PRODUCTS); + + final ContentView view = new ContentView(); + view.storeChanged(store); + + verify(store, times(1)).getContent(); + verifyNoMoreInteractions(store); + } + +} diff --git a/flux/src/test/java/com/iluwatar/flux/view/MenuViewTest.java b/flux/src/test/java/com/iluwatar/flux/view/MenuViewTest.java new file mode 100644 index 000000000..8a5d62f24 --- /dev/null +++ b/flux/src/test/java/com/iluwatar/flux/view/MenuViewTest.java @@ -0,0 +1,71 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.flux.view; + +import com.iluwatar.flux.action.Action; +import com.iluwatar.flux.action.MenuItem; +import com.iluwatar.flux.dispatcher.Dispatcher; +import com.iluwatar.flux.store.MenuStore; +import com.iluwatar.flux.store.Store; +import org.junit.Test; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +/** + * Date: 12/12/15 - 10:31 PM + * + * @author Jeroen Meulemeester + */ +public class MenuViewTest { + + @Test + public void testStoreChanged() throws Exception { + final MenuStore store = mock(MenuStore.class); + when(store.getSelected()).thenReturn(MenuItem.HOME); + + final MenuView view = new MenuView(); + view.storeChanged(store); + + verify(store, times(1)).getSelected(); + verifyNoMoreInteractions(store); + } + + @Test + public void testItemClicked() throws Exception { + final Store store = mock(Store.class); + Dispatcher.getInstance().registerStore(store); + + final MenuView view = new MenuView(); + view.itemClicked(MenuItem.PRODUCTS); + + // We should receive a menu click action and a content changed action + verify(store, times(2)).onAction(any(Action.class)); + + } + +} diff --git a/flyweight/README.md b/flyweight/README.md new file mode 100644 index 000000000..a98dced8e --- /dev/null +++ b/flyweight/README.md @@ -0,0 +1,37 @@ +--- +layout: pattern +title: Flyweight +folder: flyweight +permalink: /patterns/flyweight/ +categories: Structural +tags: + - Java + - Gang Of Four + - Difficulty-Intermediate + - Performance +--- + +## Intent +Use sharing to support large numbers of fine-grained objects +efficiently. + +![alt text](./etc/flyweight_1.png "Flyweight") + +## Applicability +The Flyweight pattern's effectiveness depends heavily on how +and where it's used. Apply the Flyweight pattern when all of the following are +true + +* an application uses a large number of objects +* storage costs are high because of the sheer quantity of objects +* most object state can be made extrinsic +* many groups of objects may be replaced by relatively few shared objects once extrinsic state is removed +* the application doesn't depend on object identity. Since flyweight objects may be shared, identity tests will return true for conceptually distinct objects. + +## Real world examples + +* [java.lang.Integer#valueOf(int)](http://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html#valueOf%28int%29) + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/flyweight/pom.xml b/flyweight/pom.xml index 70f98f9b1..959fa5548 100644 --- a/flyweight/pom.xml +++ b/flyweight/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT flyweight diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/AlchemistShop.java b/flyweight/src/main/java/com/iluwatar/flyweight/AlchemistShop.java index e87f2e6cf..507de7a6a 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/AlchemistShop.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/AlchemistShop.java @@ -1,57 +1,103 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.flyweight; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** * - * AlchemistShop holds potions on its shelves. - * It uses PotionFactory to provide the potions. + * AlchemistShop holds potions on its shelves. It uses PotionFactory to provide the potions. * */ public class AlchemistShop { - private List topShelf; - private List bottomShelf; + private List topShelf; + private List bottomShelf; - public AlchemistShop() { - topShelf = new ArrayList<>(); - bottomShelf = new ArrayList<>(); - fillShelves(); - } + /** + * Constructor + */ + public AlchemistShop() { + topShelf = new ArrayList<>(); + bottomShelf = new ArrayList<>(); + fillShelves(); + } - private void fillShelves() { + private void fillShelves() { - PotionFactory factory = new PotionFactory(); + PotionFactory factory = new PotionFactory(); - topShelf.add(factory.createPotion(PotionType.INVISIBILITY)); - topShelf.add(factory.createPotion(PotionType.INVISIBILITY)); - topShelf.add(factory.createPotion(PotionType.STRENGTH)); - topShelf.add(factory.createPotion(PotionType.HEALING)); - topShelf.add(factory.createPotion(PotionType.INVISIBILITY)); - topShelf.add(factory.createPotion(PotionType.STRENGTH)); - topShelf.add(factory.createPotion(PotionType.HEALING)); - topShelf.add(factory.createPotion(PotionType.HEALING)); + topShelf.add(factory.createPotion(PotionType.INVISIBILITY)); + topShelf.add(factory.createPotion(PotionType.INVISIBILITY)); + topShelf.add(factory.createPotion(PotionType.STRENGTH)); + topShelf.add(factory.createPotion(PotionType.HEALING)); + topShelf.add(factory.createPotion(PotionType.INVISIBILITY)); + topShelf.add(factory.createPotion(PotionType.STRENGTH)); + topShelf.add(factory.createPotion(PotionType.HEALING)); + topShelf.add(factory.createPotion(PotionType.HEALING)); - bottomShelf.add(factory.createPotion(PotionType.POISON)); - bottomShelf.add(factory.createPotion(PotionType.POISON)); - bottomShelf.add(factory.createPotion(PotionType.POISON)); - bottomShelf.add(factory.createPotion(PotionType.HOLY_WATER)); - bottomShelf.add(factory.createPotion(PotionType.HOLY_WATER)); - } + bottomShelf.add(factory.createPotion(PotionType.POISON)); + bottomShelf.add(factory.createPotion(PotionType.POISON)); + bottomShelf.add(factory.createPotion(PotionType.POISON)); + bottomShelf.add(factory.createPotion(PotionType.HOLY_WATER)); + bottomShelf.add(factory.createPotion(PotionType.HOLY_WATER)); + } - public void enumerate() { + /** + * Get a read-only list of all the items on the top shelf + * + * @return The top shelf potions + */ + public final List getTopShelf() { + return Collections.unmodifiableList(this.topShelf); + } - System.out.println("Enumerating top shelf potions\n"); + /** + * Get a read-only list of all the items on the bottom shelf + * + * @return The bottom shelf potions + */ + public final List getBottomShelf() { + return Collections.unmodifiableList(this.bottomShelf); + } - for (Potion p : topShelf) { - p.drink(); - } + /** + * Enumerate potions + */ + public void enumerate() { - System.out.println("\nEnumerating bottom shelf potions\n"); + System.out.println("Enumerating top shelf potions\n"); - for (Potion p : bottomShelf) { - p.drink(); - } - } + for (Potion p : topShelf) { + p.drink(); + } + + System.out.println("\nEnumerating bottom shelf potions\n"); + + for (Potion p : bottomShelf) { + p.drink(); + } + } } diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/App.java b/flyweight/src/main/java/com/iluwatar/flyweight/App.java index 3aa41cf2c..49c17e465 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/App.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/App.java @@ -1,23 +1,50 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.flyweight; /** * - * Flyweight pattern is useful when the program needs a huge amount of objects. - * It provides means to decrease resource usage by sharing object instances. - * - * In this example AlchemistShop has great amount of potions on its shelves. - * To fill the shelves AlchemistShop uses PotionFactory (which represents - * the Flyweight in this example). Internally PotionFactory holds a map - * of the potions and lazily creates new ones when requested. - * - * To enable safe sharing, between clients and threads, Flyweight objects must - * be immutable. Flyweight objects are by definition value objects. + * Flyweight pattern is useful when the program needs a huge amount of objects. It provides means to + * decrease resource usage by sharing object instances. + *

+ * In this example {@link AlchemistShop} has great amount of potions on its shelves. To fill the + * shelves {@link AlchemistShop} uses {@link PotionFactory} (which represents the Flyweight in this + * example). Internally {@link PotionFactory} holds a map of the potions and lazily creates new ones + * when requested. + *

+ * To enable safe sharing, between clients and threads, Flyweight objects must be immutable. + * Flyweight objects are by definition value objects. * */ public class App { - public static void main(String[] args) { - AlchemistShop alchemistShop = new AlchemistShop(); - alchemistShop.enumerate(); - } + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + AlchemistShop alchemistShop = new AlchemistShop(); + alchemistShop.enumerate(); + } } diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/HealingPotion.java b/flyweight/src/main/java/com/iluwatar/flyweight/HealingPotion.java index 397cca428..464675a61 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/HealingPotion.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/HealingPotion.java @@ -1,11 +1,36 @@ -package com.iluwatar.flyweight; - -public class HealingPotion implements Potion { - - @Override - public void drink() { - System.out.println("You feel healed. (Potion=" - + System.identityHashCode(this) + ")"); - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.flyweight; + +/** + * + * HealingPotion + * + */ +public class HealingPotion implements Potion { + + @Override + public void drink() { + System.out.println("You feel healed. (Potion=" + System.identityHashCode(this) + ")"); + } +} diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/HolyWaterPotion.java b/flyweight/src/main/java/com/iluwatar/flyweight/HolyWaterPotion.java index df4c4009e..b05b4af11 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/HolyWaterPotion.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/HolyWaterPotion.java @@ -1,11 +1,36 @@ -package com.iluwatar.flyweight; - -public class HolyWaterPotion implements Potion { - - @Override - public void drink() { - System.out.println("You feel blessed. (Potion=" - + System.identityHashCode(this) + ")"); - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.flyweight; + +/** + * + * HolyWaterPotion + * + */ +public class HolyWaterPotion implements Potion { + + @Override + public void drink() { + System.out.println("You feel blessed. (Potion=" + System.identityHashCode(this) + ")"); + } +} diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/InvisibilityPotion.java b/flyweight/src/main/java/com/iluwatar/flyweight/InvisibilityPotion.java index 2156b1017..5aeb5d3a4 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/InvisibilityPotion.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/InvisibilityPotion.java @@ -1,11 +1,36 @@ -package com.iluwatar.flyweight; - -public class InvisibilityPotion implements Potion { - - @Override - public void drink() { - System.out.println("You become invisible. (Potion=" - + System.identityHashCode(this) + ")"); - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.flyweight; + +/** + * + * InvisibilityPotion + * + */ +public class InvisibilityPotion implements Potion { + + @Override + public void drink() { + System.out.println("You become invisible. (Potion=" + System.identityHashCode(this) + ")"); + } +} diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/PoisonPotion.java b/flyweight/src/main/java/com/iluwatar/flyweight/PoisonPotion.java index 2dcfab7d3..a9d13088e 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/PoisonPotion.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/PoisonPotion.java @@ -1,11 +1,36 @@ -package com.iluwatar.flyweight; - -public class PoisonPotion implements Potion { - - @Override - public void drink() { - System.out.println("Urgh! This is poisonous. (Potion=" - + System.identityHashCode(this) + ")"); - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.flyweight; + +/** + * + * PoisonPotion + * + */ +public class PoisonPotion implements Potion { + + @Override + public void drink() { + System.out.println("Urgh! This is poisonous. (Potion=" + System.identityHashCode(this) + ")"); + } +} diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/Potion.java b/flyweight/src/main/java/com/iluwatar/flyweight/Potion.java index c4110201c..d2520c316 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/Potion.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/Potion.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.flyweight; /** @@ -7,5 +29,5 @@ package com.iluwatar.flyweight; */ public interface Potion { - void drink(); + void drink(); } diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/PotionFactory.java b/flyweight/src/main/java/com/iluwatar/flyweight/PotionFactory.java index 20ec110e3..436095081 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/PotionFactory.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/PotionFactory.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.flyweight; import java.util.EnumMap; @@ -5,48 +27,47 @@ import java.util.Map; /** * - * PotionFactory is the Flyweight in this example. - * It minimizes memory use by sharing object instances. - * It holds a map of potion instances and new potions - * are created only when none of the type already exists. + * PotionFactory is the Flyweight in this example. It minimizes memory use by sharing object + * instances. It holds a map of potion instances and new potions are created only when none of the + * type already exists. * */ public class PotionFactory { - private final Map potions; + private final Map potions; - public PotionFactory() { - potions = new EnumMap<>(PotionType.class); - } + public PotionFactory() { + potions = new EnumMap<>(PotionType.class); + } - Potion createPotion(PotionType type) { - Potion potion = potions.get(type); - if (potion == null) { - switch (type) { - case HEALING: - potion = new HealingPotion(); - potions.put(type, potion); - break; - case HOLY_WATER: - potion = new HolyWaterPotion(); - potions.put(type, potion); - break; - case INVISIBILITY: - potion = new InvisibilityPotion(); - potions.put(type, potion); - break; - case POISON: - potion = new PoisonPotion(); - potions.put(type, potion); - break; - case STRENGTH: - potion = new StrengthPotion(); - potions.put(type, potion); - break; - default: - break; - } - } - return potion; - } + Potion createPotion(PotionType type) { + Potion potion = potions.get(type); + if (potion == null) { + switch (type) { + case HEALING: + potion = new HealingPotion(); + potions.put(type, potion); + break; + case HOLY_WATER: + potion = new HolyWaterPotion(); + potions.put(type, potion); + break; + case INVISIBILITY: + potion = new InvisibilityPotion(); + potions.put(type, potion); + break; + case POISON: + potion = new PoisonPotion(); + potions.put(type, potion); + break; + case STRENGTH: + potion = new StrengthPotion(); + potions.put(type, potion); + break; + default: + break; + } + } + return potion; + } } diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/PotionType.java b/flyweight/src/main/java/com/iluwatar/flyweight/PotionType.java index bbb9b6521..89c6fdbe3 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/PotionType.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/PotionType.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.flyweight; /** @@ -7,5 +29,5 @@ package com.iluwatar.flyweight; */ public enum PotionType { - HEALING, INVISIBILITY, STRENGTH, HOLY_WATER, POISON + HEALING, INVISIBILITY, STRENGTH, HOLY_WATER, POISON } diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/StrengthPotion.java b/flyweight/src/main/java/com/iluwatar/flyweight/StrengthPotion.java index a1ffcdc53..2c21e7df1 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/StrengthPotion.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/StrengthPotion.java @@ -1,10 +1,36 @@ -package com.iluwatar.flyweight; - -public class StrengthPotion implements Potion { - - @Override - public void drink() { - System.out.println("You feel strong. (Potion=" - + System.identityHashCode(this) + ")"); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.flyweight; + +/** + * + * StrengthPotion + * + */ +public class StrengthPotion implements Potion { + + @Override + public void drink() { + System.out.println("You feel strong. (Potion=" + System.identityHashCode(this) + ")"); + } +} diff --git a/flyweight/src/test/java/com/iluwatar/flyweight/AlchemistShopTest.java b/flyweight/src/test/java/com/iluwatar/flyweight/AlchemistShopTest.java new file mode 100644 index 000000000..efb0a6e2e --- /dev/null +++ b/flyweight/src/test/java/com/iluwatar/flyweight/AlchemistShopTest.java @@ -0,0 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.flyweight; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/12/15 - 10:54 PM + * + * @author Jeroen Meulemeester + */ +public class AlchemistShopTest { + + @Test + public void testShop() throws Exception { + final AlchemistShop shop = new AlchemistShop(); + + final List bottomShelf = shop.getBottomShelf(); + assertNotNull(bottomShelf); + assertEquals(5, bottomShelf.size()); + + final List topShelf = shop.getTopShelf(); + assertNotNull(topShelf); + assertEquals(8, topShelf.size()); + + final List allPotions = new ArrayList<>(); + allPotions.addAll(topShelf); + allPotions.addAll(bottomShelf); + + // There are 13 potion instances, but only 5 unique instance types + assertEquals(13, allPotions.size()); + assertEquals(5, allPotions.stream().map(System::identityHashCode).distinct().count()); + + } + +} diff --git a/flyweight/src/test/java/com/iluwatar/flyweight/AppTest.java b/flyweight/src/test/java/com/iluwatar/flyweight/AppTest.java index 2c93d0e10..282311920 100644 --- a/flyweight/src/test/java/com/iluwatar/flyweight/AppTest.java +++ b/flyweight/src/test/java/com/iluwatar/flyweight/AppTest.java @@ -1,14 +1,39 @@ -package com.iluwatar.flyweight; - -import org.junit.Test; - -import com.iluwatar.flyweight.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.flyweight; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/front-controller/README.md b/front-controller/README.md new file mode 100644 index 000000000..a462a08e0 --- /dev/null +++ b/front-controller/README.md @@ -0,0 +1,34 @@ +--- +layout: pattern +title: Front Controller +folder: front-controller +permalink: /patterns/front-controller/ +categories: Presentation Tier +tags: + - Java + - Difficulty-Intermediate +--- + +## Intent +Introduce a common handler for all requests for a web site. This +way we can encapsulate common functionality such as security, +internationalization, routing and logging in a single place. + +![alt text](./etc/front-controller.png "Front Controller") + +## Applicability +Use the Front Controller pattern when + +* you want to encapsulate common request handling functionality in single place +* you want to implements dynamic request handling i.e. change routing without modifying code +* make web server configuration portable, you only need to register the handler web server specific way + +## Real world examples + +* [Apache Struts](https://struts.apache.org/) + +## Credits + +* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) +* [Presentation Tier Patterns](http://www.javagyan.com/tutorials/corej2eepatterns/presentation-tier-patterns) +* [Patterns of Enterprise Application Architecture](http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420) diff --git a/front-controller/etc/front-controller.png b/front-controller/etc/front-controller.png new file mode 100644 index 000000000..77c14ef01 Binary files /dev/null and b/front-controller/etc/front-controller.png differ diff --git a/front-controller/etc/front-controller.ucls b/front-controller/etc/front-controller.ucls new file mode 100644 index 000000000..11cdf66ec --- /dev/null +++ b/front-controller/etc/front-controller.ucls @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/front-controller/pom.xml b/front-controller/pom.xml new file mode 100644 index 000000000..2a448cffe --- /dev/null +++ b/front-controller/pom.xml @@ -0,0 +1,48 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + front-controller + + + junit + junit + test + + + org.mockito + mockito-core + test + + + diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/App.java b/front-controller/src/main/java/com/iluwatar/front/controller/App.java new file mode 100644 index 000000000..909578b3b --- /dev/null +++ b/front-controller/src/main/java/com/iluwatar/front/controller/App.java @@ -0,0 +1,57 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.front.controller; + +/** + * + * The Front Controller is a presentation tier pattern. Essentially it defines a controller that + * handles all requests for a web site. + *

+ * The Front Controller pattern consolidates request handling through a single handler object ( + * {@link FrontController}). This object can carry out the common the behavior such as + * authorization, request logging and routing requests to corresponding views. + *

+ * Typically the requests are mapped to command objects ({@link Command}) which then display the + * correct view ({@link View}). + *

+ * In this example we have implemented two views: {@link ArcherView} and {@link CatapultView}. These + * are displayed by sending correct request to the {@link FrontController} object. For example, the + * {@link ArcherView} gets displayed when {@link FrontController} receives request "Archer". When + * the request is unknown, we display the error view ({@link ErrorView}). + * + */ +public class App { + + /** + * Program entry point + * + * @param args + * command line args + */ + public static void main(String[] args) { + FrontController controller = new FrontController(); + controller.handleRequest("Archer"); + controller.handleRequest("Catapult"); + controller.handleRequest("foobar"); + } +} diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/ApplicationException.java b/front-controller/src/main/java/com/iluwatar/front/controller/ApplicationException.java new file mode 100644 index 000000000..aa50fe2ce --- /dev/null +++ b/front-controller/src/main/java/com/iluwatar/front/controller/ApplicationException.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.front.controller; + +/** + * + * Custom exception type + * + */ +public class ApplicationException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public ApplicationException(Throwable cause) { + super(cause); + } +} diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/ArcherCommand.java b/front-controller/src/main/java/com/iluwatar/front/controller/ArcherCommand.java new file mode 100644 index 000000000..a1a391979 --- /dev/null +++ b/front-controller/src/main/java/com/iluwatar/front/controller/ArcherCommand.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.front.controller; + +/** + * + * Command for archers. + * + */ +public class ArcherCommand implements Command { + + @Override + public void process() { + new ArcherView().display(); + } +} diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/ArcherView.java b/front-controller/src/main/java/com/iluwatar/front/controller/ArcherView.java new file mode 100644 index 000000000..a7cf8ca27 --- /dev/null +++ b/front-controller/src/main/java/com/iluwatar/front/controller/ArcherView.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.front.controller; + +/** + * + * View for archers. + * + */ +public class ArcherView implements View { + + @Override + public void display() { + System.out.println("Displaying archers"); + } +} diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/CatapultCommand.java b/front-controller/src/main/java/com/iluwatar/front/controller/CatapultCommand.java new file mode 100644 index 000000000..c4b3c6ea3 --- /dev/null +++ b/front-controller/src/main/java/com/iluwatar/front/controller/CatapultCommand.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.front.controller; + +/** + * + * Command for catapults. + * + */ +public class CatapultCommand implements Command { + + @Override + public void process() { + new CatapultView().display(); + } +} diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/CatapultView.java b/front-controller/src/main/java/com/iluwatar/front/controller/CatapultView.java new file mode 100644 index 000000000..90e4c7896 --- /dev/null +++ b/front-controller/src/main/java/com/iluwatar/front/controller/CatapultView.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.front.controller; + +/** + * + * View for catapults. + * + */ +public class CatapultView implements View { + + @Override + public void display() { + System.out.println("Displaying catapults"); + } +} diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/Command.java b/front-controller/src/main/java/com/iluwatar/front/controller/Command.java new file mode 100644 index 000000000..ed49572e4 --- /dev/null +++ b/front-controller/src/main/java/com/iluwatar/front/controller/Command.java @@ -0,0 +1,33 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.front.controller; + +/** + * + * Commands are the intermediary between requests and views. + * + */ +public interface Command { + + void process(); +} diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/ErrorView.java b/front-controller/src/main/java/com/iluwatar/front/controller/ErrorView.java new file mode 100644 index 000000000..cbfd4bd2e --- /dev/null +++ b/front-controller/src/main/java/com/iluwatar/front/controller/ErrorView.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.front.controller; + +/** + * + * View for errors. + * + */ +public class ErrorView implements View { + + @Override + public void display() { + System.out.println("Error 500"); + } +} diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/FrontController.java b/front-controller/src/main/java/com/iluwatar/front/controller/FrontController.java new file mode 100644 index 000000000..6e2cccb0f --- /dev/null +++ b/front-controller/src/main/java/com/iluwatar/front/controller/FrontController.java @@ -0,0 +1,56 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.front.controller; + +/** + * + * FrontController is the handler class that takes in all the requests and renders the correct + * response. + * + */ +public class FrontController { + + public void handleRequest(String request) { + Command command = getCommand(request); + command.process(); + } + + private Command getCommand(String request) { + Class commandClass = getCommandClass(request); + try { + return (Command) commandClass.newInstance(); + } catch (Exception e) { + throw new ApplicationException(e); + } + } + + private static Class getCommandClass(String request) { + Class result; + try { + result = Class.forName("com.iluwatar.front.controller." + request + "Command"); + } catch (ClassNotFoundException e) { + result = UnknownCommand.class; + } + return result; + } +} diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/UnknownCommand.java b/front-controller/src/main/java/com/iluwatar/front/controller/UnknownCommand.java new file mode 100644 index 000000000..6e5b33987 --- /dev/null +++ b/front-controller/src/main/java/com/iluwatar/front/controller/UnknownCommand.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.front.controller; + +/** + * + * Default command in case the mapping is not successful. + * + */ +public class UnknownCommand implements Command { + + @Override + public void process() { + new ErrorView().display(); + } +} diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/View.java b/front-controller/src/main/java/com/iluwatar/front/controller/View.java new file mode 100644 index 000000000..536bb9750 --- /dev/null +++ b/front-controller/src/main/java/com/iluwatar/front/controller/View.java @@ -0,0 +1,33 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.front.controller; + +/** + * + * Views are the representations rendered for the user. + * + */ +public interface View { + + void display(); +} diff --git a/front-controller/src/test/java/com/iluwatar/front/controller/AppTest.java b/front-controller/src/test/java/com/iluwatar/front/controller/AppTest.java new file mode 100644 index 000000000..8eec47b0a --- /dev/null +++ b/front-controller/src/test/java/com/iluwatar/front/controller/AppTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.front.controller; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/front-controller/src/test/java/com/iluwatar/front/controller/ApplicationExceptionTest.java b/front-controller/src/test/java/com/iluwatar/front/controller/ApplicationExceptionTest.java new file mode 100644 index 000000000..26157acde --- /dev/null +++ b/front-controller/src/test/java/com/iluwatar/front/controller/ApplicationExceptionTest.java @@ -0,0 +1,42 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.front.controller; + +import static org.junit.Assert.assertSame; + +import org.junit.Test; + +/** + * Date: 12/13/15 - 1:35 PM + * + * @author Jeroen Meulemeester + */ +public class ApplicationExceptionTest { + + @Test + public void testCause() throws Exception { + final Exception cause = new Exception(); + assertSame(cause, new ApplicationException(cause).getCause()); + } + +} \ No newline at end of file diff --git a/front-controller/src/test/java/com/iluwatar/front/controller/CommandTest.java b/front-controller/src/test/java/com/iluwatar/front/controller/CommandTest.java new file mode 100644 index 000000000..c4d9ae625 --- /dev/null +++ b/front-controller/src/test/java/com/iluwatar/front/controller/CommandTest.java @@ -0,0 +1,84 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.front.controller; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/13/15 - 1:39 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class CommandTest extends StdOutTest { + + @Parameters + public static List data() { + final List parameters = new ArrayList<>(); + parameters.add(new Object[]{"Archer", "Displaying archers"}); + parameters.add(new Object[]{"Catapult", "Displaying catapults"}); + parameters.add(new Object[]{"NonExistentCommand", "Error 500"}); + return parameters; + } + + /** + * The view that's been tested + */ + private final String request; + + /** + * The expected display message + */ + private final String displayMessage; + + /** + * Create a new instance of the {@link CommandTest} with the given view and expected message + * + * @param request The request that's been tested + * @param displayMessage The expected display message + */ + public CommandTest(final String request, final String displayMessage) { + this.displayMessage = displayMessage; + this.request = request; + } + + @Test + public void testDisplay() { + final FrontController frontController = new FrontController(); + verifyZeroInteractions(getStdOutMock()); + frontController.handleRequest(request); + verify(getStdOutMock()).println(displayMessage); + verifyNoMoreInteractions(getStdOutMock()); + } + +} diff --git a/front-controller/src/test/java/com/iluwatar/front/controller/FrontControllerTest.java b/front-controller/src/test/java/com/iluwatar/front/controller/FrontControllerTest.java new file mode 100644 index 000000000..0822ffcd0 --- /dev/null +++ b/front-controller/src/test/java/com/iluwatar/front/controller/FrontControllerTest.java @@ -0,0 +1,83 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.front.controller; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/13/15 - 1:39 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class FrontControllerTest extends StdOutTest { + + @Parameters + public static List data() { + final List parameters = new ArrayList<>(); + parameters.add(new Object[]{new ArcherCommand(), "Displaying archers"}); + parameters.add(new Object[]{new CatapultCommand(), "Displaying catapults"}); + parameters.add(new Object[]{new UnknownCommand(), "Error 500"}); + return parameters; + } + + /** + * The view that's been tested + */ + private final Command command; + + /** + * The expected display message + */ + private final String displayMessage; + + /** + * Create a new instance of the {@link FrontControllerTest} with the given view and expected message + * + * @param command The command that's been tested + * @param displayMessage The expected display message + */ + public FrontControllerTest(final Command command, final String displayMessage) { + this.displayMessage = displayMessage; + this.command = command; + } + + @Test + public void testDisplay() { + verifyZeroInteractions(getStdOutMock()); + this.command.process(); + verify(getStdOutMock()).println(displayMessage); + verifyNoMoreInteractions(getStdOutMock()); + } + +} diff --git a/front-controller/src/test/java/com/iluwatar/front/controller/StdOutTest.java b/front-controller/src/test/java/com/iluwatar/front/controller/StdOutTest.java new file mode 100644 index 000000000..bc32a1b3c --- /dev/null +++ b/front-controller/src/test/java/com/iluwatar/front/controller/StdOutTest.java @@ -0,0 +1,76 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.front.controller; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since the actions of the views don't have + * any influence on any other accessible objects, except for writing to std-out using {@link + * System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/front-controller/src/test/java/com/iluwatar/front/controller/ViewTest.java b/front-controller/src/test/java/com/iluwatar/front/controller/ViewTest.java new file mode 100644 index 000000000..cdabd66ef --- /dev/null +++ b/front-controller/src/test/java/com/iluwatar/front/controller/ViewTest.java @@ -0,0 +1,83 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.front.controller; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/13/15 - 1:39 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class ViewTest extends StdOutTest { + + @Parameters + public static List data() { + final List parameters = new ArrayList<>(); + parameters.add(new Object[]{new ArcherView(), "Displaying archers"}); + parameters.add(new Object[]{new CatapultView(), "Displaying catapults"}); + parameters.add(new Object[]{new ErrorView(), "Error 500"}); + return parameters; + } + + /** + * The view that's been tested + */ + private final View view; + + /** + * The expected display message + */ + private final String displayMessage; + + /** + * Create a new instance of the {@link ViewTest} with the given view and expected message + * + * @param view The view that's been tested + * @param displayMessage The expected display message + */ + public ViewTest(final View view, final String displayMessage) { + this.displayMessage = displayMessage; + this.view = view; + } + + @Test + public void testDisplay() { + verifyZeroInteractions(getStdOutMock()); + this.view.display(); + verify(getStdOutMock()).println(displayMessage); + verifyNoMoreInteractions(getStdOutMock()); + } + +} \ No newline at end of file diff --git a/half-sync-half-async/README.md b/half-sync-half-async/README.md new file mode 100644 index 000000000..8a091f813 --- /dev/null +++ b/half-sync-half-async/README.md @@ -0,0 +1,37 @@ +--- +layout: pattern +title: Half-Sync/Half-Async +folder: half-sync-half-async +permalink: /patterns/half-sync-half-async/ +categories: Concurrency +tags: + - Java + - Difficulty-Intermediate +--- + +## Intent +The Half-Sync/Half-Async pattern decouples synchronous I/O from +asynchronous I/O in a system to simplify concurrent programming effort without +degrading execution efficiency. + +![Half-Sync/Half-Async class diagram](./etc/half-sync-half-async.png) + +## Applicability +Use Half-Sync/Half-Async pattern when + +* a system possesses following characteristics: + * the system must perform tasks in response to external events that occur asynchronously, like hardware interrupts in OS + * it is inefficient to dedicate separate thread of control to perform synchronous I/O for each external source of event + * the higher level tasks in the system can be simplified significantly if I/O is performed synchronously. +* one or more tasks in a system must run in a single thread of control, while other tasks may benefit from multi-threading. + +## Real world examples + +* [BSD Unix networking subsystem](http://www.cs.wustl.edu/~schmidt/PDF/PLoP-95.pdf) +* [Real Time CORBA](http://www.omg.org/news/meetings/workshops/presentations/realtime2001/4-3_Pyarali_thread-pool.pdf) +* [Android AsyncTask framework](http://developer.android.com/reference/android/os/AsyncTask.html) + +## Credits + +* [Douglas C. Schmidt and Charles D. Cranor - Half Sync/Half Async](http://www.cs.wustl.edu/~schmidt/PDF/PLoP-95.pdf) +* [Pattern Oriented Software Architecture Vol I-V](http://www.amazon.com/Pattern-Oriented-Software-Architecture-Volume-Patterns/dp/0471958697) diff --git a/half-sync-half-async/etc/half-sync-half-async.png b/half-sync-half-async/etc/half-sync-half-async.png new file mode 100644 index 000000000..84658dfde Binary files /dev/null and b/half-sync-half-async/etc/half-sync-half-async.png differ diff --git a/half-sync-half-async/etc/half-sync-half-async.ucls b/half-sync-half-async/etc/half-sync-half-async.ucls new file mode 100644 index 000000000..5b9941872 --- /dev/null +++ b/half-sync-half-async/etc/half-sync-half-async.ucls @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/half-sync-half-async/pom.xml b/half-sync-half-async/pom.xml new file mode 100644 index 000000000..076dfb44d --- /dev/null +++ b/half-sync-half-async/pom.xml @@ -0,0 +1,47 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + half-sync-half-async + + + junit + junit + test + + + org.mockito + mockito-core + test + + + diff --git a/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/App.java b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/App.java new file mode 100644 index 000000000..17839bb32 --- /dev/null +++ b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/App.java @@ -0,0 +1,148 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.halfsynchalfasync; + +import java.util.concurrent.LinkedBlockingQueue; + +/** + * + * This application demonstrates Half-Sync/Half-Async pattern. Key parts of the pattern are + * {@link AsyncTask} and {@link AsynchronousService}. + * + *

+ * PROBLEM
+ * A concurrent system have a mixture of short duration, mid duration and long duration tasks. Mid + * or long duration tasks should be performed asynchronously to meet quality of service + * requirements. + * + *

+ * INTENT
+ * The intent of this pattern is to separate the the synchronous and asynchronous processing in the + * concurrent application by introducing two intercommunicating layers - one for sync and one for + * async. This simplifies the programming without unduly affecting the performance. + * + *

+ * APPLICABILITY
+ * UNIX network subsystems - In operating systems network operations are carried out + * asynchronously with help of hardware level interrupts.
+ * CORBA - At the asynchronous layer one thread is associated with each socket that is connected + * to the client. Thread blocks waiting for CORBA requests from the client. On receiving request it + * is inserted in the queuing layer which is then picked up by synchronous layer which processes the + * request and sends response back to the client.
+ * Android AsyncTask framework - Framework provides a way to execute long running blocking + * calls, such as downloading a file, in background threads so that the UI thread remains free to + * respond to user inputs.
+ * + *

+ * IMPLEMENTATION
+ * The main method creates an asynchronous service which does not block the main thread while the + * task is being performed. The main thread continues its work which is similar to Async Method + * Invocation pattern. The difference between them is that there is a queuing layer between + * Asynchronous layer and synchronous layer, which allows for different communication patterns + * between both layers. Such as Priority Queue can be used as queuing layer to prioritize the way + * tasks are executed. Our implementation is just one simple way of implementing this pattern, there + * are many variants possible as described in its applications. + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + AsynchronousService service = new AsynchronousService(new LinkedBlockingQueue<>()); + /* + * A new task to calculate sum is received but as this is main thread, it should not block. So + * it passes it to the asynchronous task layer to compute and proceeds with handling other + * incoming requests. This is particularly useful when main thread is waiting on Socket to + * receive new incoming requests and does not wait for particular request to be completed before + * responding to new request. + */ + service.execute(new ArithmeticSumTask(1000)); + + /* + * New task received, lets pass that to async layer for computation. So both requests will be + * executed in parallel. + */ + service.execute(new ArithmeticSumTask(500)); + service.execute(new ArithmeticSumTask(2000)); + service.execute(new ArithmeticSumTask(1)); + } + + /** + * + * ArithmeticSumTask + * + */ + static class ArithmeticSumTask implements AsyncTask { + private long n; + + public ArithmeticSumTask(long n) { + this.n = n; + } + + /* + * This is the long running task that is performed in background. In our example the long + * running task is calculating arithmetic sum with artificial delay. + */ + @Override + public Long call() throws Exception { + return ap(n); + } + + /* + * This will be called in context of the main thread where some validations can be done + * regarding the inputs. Such as it must be greater than 0. It's a small computation which can + * be performed in main thread. If we did validated the input in background thread then we pay + * the cost of context switching which is much more than validating it in main thread. + */ + @Override + public void onPreCall() { + if (n < 0) { + throw new IllegalArgumentException("n is less than 0"); + } + } + + @Override + public void onPostCall(Long result) { + // Handle the result of computation + System.out.println(result); + } + + @Override + public void onError(Throwable throwable) { + throw new IllegalStateException("Should not occur"); + } + } + + private static long ap(long i) { + try { + Thread.sleep(i); + } catch (InterruptedException e) { + System.out.println(e); + } + return i * (i + 1) / 2; + } +} diff --git a/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsyncTask.java b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsyncTask.java new file mode 100644 index 000000000..91e36069e --- /dev/null +++ b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsyncTask.java @@ -0,0 +1,66 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.halfsynchalfasync; + +import java.util.concurrent.Callable; + +/** + * Represents some computation that is performed asynchronously and its result. The computation is + * typically done is background threads and the result is posted back in form of callback. The + * callback does not implement {@code isComplete}, {@code cancel} as it is out of scope of this + * pattern. + * + * @param type of result + */ +public interface AsyncTask extends Callable { + /** + * Is called in context of caller thread before call to {@link #call()}. Large tasks should not be + * performed in this method as it will block the caller thread. Small tasks such as validations + * can be performed here so that the performance penalty of context switching is not incurred in + * case of invalid requests. + */ + void onPreCall(); + + /** + * A callback called after the result is successfully computed by {@link #call()}. In our + * implementation this method is called in context of background thread but in some variants, such + * as Android where only UI thread can change the state of UI widgets, this method is called in + * context of UI thread. + */ + void onPostCall(O result); + + /** + * A callback called if computing the task resulted in some exception. This method is called when + * either of {@link #call()} or {@link #onPreCall()} throw any exception. + * + * @param throwable error cause + */ + void onError(Throwable throwable); + + /** + * This is where the computation of task should reside. This method is called in context of + * background thread. + */ + @Override + O call() throws Exception; +} diff --git a/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsynchronousService.java b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsynchronousService.java new file mode 100644 index 000000000..b42f551f2 --- /dev/null +++ b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsynchronousService.java @@ -0,0 +1,98 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.halfsynchalfasync; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.FutureTask; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * This is the asynchronous layer which does not block when a new request arrives. It just passes + * the request to the synchronous layer which consists of a queue i.e. a {@link BlockingQueue} and a + * pool of threads i.e. {@link ThreadPoolExecutor}. Out of this pool of worker threads one of the + * thread picks up the task and executes it synchronously in background and the result is posted + * back to the caller via callback. + */ +public class AsynchronousService { + + /* + * This represents the queuing layer as well as synchronous layer of the pattern. The thread pool + * contains worker threads which execute the tasks in blocking/synchronous manner. Long running + * tasks should be performed in the background which does not affect the performance of main + * thread. + */ + private ExecutorService service; + + /** + * Creates an asynchronous service using {@code workQueue} as communication channel between + * asynchronous layer and synchronous layer. Different types of queues such as Priority queue, can + * be used to control the pattern of communication between the layers. + */ + public AsynchronousService(BlockingQueue workQueue) { + service = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, workQueue); + } + + + /** + * A non-blocking method which performs the task provided in background and returns immediately. + *

+ * On successful completion of task the result is posted back using callback method + * {@link AsyncTask#onPostCall(Object)}, if task execution is unable to complete normally due to + * some exception then the reason for error is posted back using callback method + * {@link AsyncTask#onError(Throwable)}. + *

+ * NOTE: The results are posted back in the context of background thread in this implementation. + */ + public void execute(final AsyncTask task) { + try { + // some small tasks such as validation can be performed here. + task.onPreCall(); + } catch (Exception e) { + task.onError(e); + return; + } + + service.submit(new FutureTask(task) { + @Override + protected void done() { + super.done(); + try { + /* + * called in context of background thread. There is other variant possible where result is + * posted back and sits in the queue of caller thread which then picks it up for + * processing. An example of such a system is Android OS, where the UI elements can only + * be updated using UI thread. So result must be posted back in UI thread. + */ + task.onPostCall(get()); + } catch (InterruptedException e) { + // should not occur + } catch (ExecutionException e) { + task.onError(e.getCause()); + } + } + }); + } +} diff --git a/half-sync-half-async/src/test/java/com/iluwatar/halfsynchalfasync/AppTest.java b/half-sync-half-async/src/test/java/com/iluwatar/halfsynchalfasync/AppTest.java new file mode 100644 index 000000000..e791cc310 --- /dev/null +++ b/half-sync-half-async/src/test/java/com/iluwatar/halfsynchalfasync/AppTest.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.halfsynchalfasync; + +import java.util.concurrent.ExecutionException; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() throws InterruptedException, ExecutionException { + App.main(null); + } +} diff --git a/half-sync-half-async/src/test/java/com/iluwatar/halfsynchalfasync/AsynchronousServiceTest.java b/half-sync-half-async/src/test/java/com/iluwatar/halfsynchalfasync/AsynchronousServiceTest.java new file mode 100644 index 000000000..7fcbd1541 --- /dev/null +++ b/half-sync-half-async/src/test/java/com/iluwatar/halfsynchalfasync/AsynchronousServiceTest.java @@ -0,0 +1,94 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.halfsynchalfasync; + +import org.junit.Test; +import org.mockito.InOrder; + +import java.io.IOException; +import java.util.concurrent.LinkedBlockingQueue; + +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; + +/** + * Date: 12/12/15 - 11:15 PM + * + * @author Jeroen Meulemeester + */ +public class AsynchronousServiceTest { + + @Test + public void testPerfectExecution() throws Exception { + final AsynchronousService service = new AsynchronousService(new LinkedBlockingQueue<>()); + final AsyncTask task = mock(AsyncTask.class); + final Object result = new Object(); + when(task.call()).thenReturn(result); + service.execute(task); + + verify(task, timeout(2000)).onPostCall(eq(result)); + + final InOrder inOrder = inOrder(task); + inOrder.verify(task, times(1)).onPreCall(); + inOrder.verify(task, times(1)).call(); + inOrder.verify(task, times(1)).onPostCall(eq(result)); + + verifyNoMoreInteractions(task); + } + + @Test + public void testCallException() throws Exception { + final AsynchronousService service = new AsynchronousService(new LinkedBlockingQueue<>()); + final AsyncTask task = mock(AsyncTask.class); + final IOException exception = new IOException(); + when(task.call()).thenThrow(exception); + service.execute(task); + + verify(task, timeout(2000)).onError(eq(exception)); + + final InOrder inOrder = inOrder(task); + inOrder.verify(task, times(1)).onPreCall(); + inOrder.verify(task, times(1)).call(); + inOrder.verify(task, times(1)).onError(exception); + + verifyNoMoreInteractions(task); + } + + @Test + public void testPreCallException() throws Exception { + final AsynchronousService service = new AsynchronousService(new LinkedBlockingQueue<>()); + final AsyncTask task = mock(AsyncTask.class); + final IllegalStateException exception = new IllegalStateException(); + doThrow(exception).when(task).onPreCall(); + service.execute(task); + + verify(task, timeout(2000)).onError(eq(exception)); + + final InOrder inOrder = inOrder(task); + inOrder.verify(task, times(1)).onPreCall(); + inOrder.verify(task, times(1)).onError(exception); + + verifyNoMoreInteractions(task); + } + +} \ No newline at end of file diff --git a/hexagonal/README.md b/hexagonal/README.md new file mode 100644 index 000000000..b1d0a7948 --- /dev/null +++ b/hexagonal/README.md @@ -0,0 +1,34 @@ +--- +layout: pattern +title: Hexagonal Architecture +folder: hexagonal +permalink: /patterns/hexagonal/ +categories: Architectural +tags: + - Java + - Difficulty-Expert +--- + +## Also known as +* Ports and Adapters +* Clean Architecture +* Onion Architecture + +## Intent +Allow an application to equally be driven by users, programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases. + +![Hexagonal Architecture class diagram](./etc/hexagonal.png) + +## Applicability +Use Hexagonal Architecture pattern when + +* it is important that the application is fully testable +* you use Domain Driven Design methodology and/or Microservices architectural style + +## Real world examples + +* [Apache Isis](https://isis.apache.org/) + +## Credits + +* [Alistair Cockburn - Hexagonal Architecture](http://alistair.cockburn.us/Hexagonal+architecture) diff --git a/hexagonal/etc/hexagonal.png b/hexagonal/etc/hexagonal.png new file mode 100644 index 000000000..8c03d375f Binary files /dev/null and b/hexagonal/etc/hexagonal.png differ diff --git a/hexagonal/etc/hexagonal.ucls b/hexagonal/etc/hexagonal.ucls new file mode 100644 index 000000000..b54e2abf7 --- /dev/null +++ b/hexagonal/etc/hexagonal.ucls @@ -0,0 +1,325 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hexagonal/pom.xml b/hexagonal/pom.xml new file mode 100644 index 000000000..e9e2a502d --- /dev/null +++ b/hexagonal/pom.xml @@ -0,0 +1,43 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + hexagonal + + + junit + junit + test + + + diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java new file mode 100644 index 000000000..3ae55b706 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -0,0 +1,151 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import com.iluwatar.hexagonal.administration.LotteryAdministration; +import com.iluwatar.hexagonal.administration.LotteryAdministrationImpl; +import com.iluwatar.hexagonal.banking.WireTransfersImpl; +import com.iluwatar.hexagonal.domain.LotteryConstants; +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.PlayerDetails; +import com.iluwatar.hexagonal.service.LotteryService; +import com.iluwatar.hexagonal.service.LotteryServiceImpl; + +/** + * + * Hexagonal Architecture pattern decouples the application core from the + * services it uses. This allows the services to be plugged in and the + * application will run with or without the services.

+ * + * The core logic, or business logic, of an application consists of the + * algorithms that are essential to its purpose. They implement the use + * cases that are the heart of the application. When you change them, you + * change the essence of the application.

+ * + * The services are not essential. They can be replaced without changing + * the purpose of the application. Examples: database access and other + * types of storage, user interface components, e-mail and other + * communication components, hardware devices.

+ * + * This example demonstrates Hexagonal Architecture with a lottery system. + * The application core is separate from the services that drive it and + * from the services it uses.

+ * + * The primary ports for the application are {@link LotteryAdministration} + * through which the lottery round is initiated and run and + * {@link LotteryService} that allows players to submit lottery tickets for + * the draw.

+ * + * The secondary ports that application core uses are {@link WireTransfers} + * which is a banking service, {@link LotteryNotifications} that delivers + * notifications as lottery events occur and {@link LotteryTicketRepository} + * that is the storage for the lottery tickets. + * + */ +public class App { + + private static List allPlayerDetails; + + static { + allPlayerDetails = new ArrayList<>(); + allPlayerDetails.add(PlayerDetails.create("john@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("mary@google.com", "234-987", "+23452346")); + allPlayerDetails.add(PlayerDetails.create("steve@google.com", "833-836", "+63457543")); + allPlayerDetails.add(PlayerDetails.create("wayne@google.com", "319-826", "+24626")); + allPlayerDetails.add(PlayerDetails.create("johnie@google.com", "983-322", "+3635635")); + allPlayerDetails.add(PlayerDetails.create("andy@google.com", "934-734", "+0898245")); + allPlayerDetails.add(PlayerDetails.create("richard@google.com", "536-738", "+09845325")); + allPlayerDetails.add(PlayerDetails.create("kevin@google.com", "453-936", "+2423532")); + allPlayerDetails.add(PlayerDetails.create("arnold@google.com", "114-988", "+5646346524")); + allPlayerDetails.add(PlayerDetails.create("ian@google.com", "663-765", "+928394235")); + allPlayerDetails.add(PlayerDetails.create("robin@google.com", "334-763", "+35448")); + allPlayerDetails.add(PlayerDetails.create("ted@google.com", "735-964", "+98752345")); + allPlayerDetails.add(PlayerDetails.create("larry@google.com", "734-853", "+043842423")); + allPlayerDetails.add(PlayerDetails.create("calvin@google.com", "334-746", "+73294135")); + allPlayerDetails.add(PlayerDetails.create("jacob@google.com", "444-766", "+358042354")); + allPlayerDetails.add(PlayerDetails.create("edwin@google.com", "895-345", "+9752435")); + allPlayerDetails.add(PlayerDetails.create("mary@google.com", "760-009", "+34203542")); + allPlayerDetails.add(PlayerDetails.create("lolita@google.com", "425-907", "+9872342")); + allPlayerDetails.add(PlayerDetails.create("bruno@google.com", "023-638", "+673824122")); + allPlayerDetails.add(PlayerDetails.create("peter@google.com", "335-886", "+5432503945")); + allPlayerDetails.add(PlayerDetails.create("warren@google.com", "225-946", "+9872341324")); + allPlayerDetails.add(PlayerDetails.create("monica@google.com", "265-748", "+134124")); + allPlayerDetails.add(PlayerDetails.create("ollie@google.com", "190-045", "+34453452")); + allPlayerDetails.add(PlayerDetails.create("yngwie@google.com", "241-465", "+9897641231")); + allPlayerDetails.add(PlayerDetails.create("lars@google.com", "746-936", "+42345298345")); + allPlayerDetails.add(PlayerDetails.create("bobbie@google.com", "946-384", "+79831742")); + allPlayerDetails.add(PlayerDetails.create("tyron@google.com", "310-992", "+0498837412")); + allPlayerDetails.add(PlayerDetails.create("tyrell@google.com", "032-045", "+67834134")); + allPlayerDetails.add(PlayerDetails.create("nadja@google.com", "000-346", "+498723")); + allPlayerDetails.add(PlayerDetails.create("wendy@google.com", "994-989", "+987324454")); + allPlayerDetails.add(PlayerDetails.create("luke@google.com", "546-634", "+987642435")); + allPlayerDetails.add(PlayerDetails.create("bjorn@google.com", "342-874", "+7834325")); + allPlayerDetails.add(PlayerDetails.create("lisa@google.com", "024-653", "+980742154")); + allPlayerDetails.add(PlayerDetails.create("anton@google.com", "834-935", "+876423145")); + allPlayerDetails.add(PlayerDetails.create("bruce@google.com", "284-936", "+09843212345")); + allPlayerDetails.add(PlayerDetails.create("ray@google.com", "843-073", "+678324123")); + allPlayerDetails.add(PlayerDetails.create("ron@google.com", "637-738", "+09842354")); + allPlayerDetails.add(PlayerDetails.create("xavier@google.com", "143-947", "+375245")); + allPlayerDetails.add(PlayerDetails.create("harriet@google.com", "842-404", "+131243252")); + WireTransfersImpl wireTransfers = new WireTransfersImpl(); + Random random = new Random(); + for (int i = 0; i < allPlayerDetails.size(); i++) { + wireTransfers.setFunds(allPlayerDetails.get(i).getBankAccount(), + random.nextInt(LotteryConstants.PLAYER_MAX_SALDO)); + } + } + + /** + * Program entry point + */ + public static void main(String[] args) { + // start new lottery round + LotteryAdministration administartion = new LotteryAdministrationImpl(); + administartion.resetLottery(); + + // submit some lottery tickets + LotteryServiceImpl service = new LotteryServiceImpl(); + submitTickets(service, 20); + + // perform lottery + administartion.performLottery(); + } + + private static void submitTickets(LotteryService lotteryService, int numTickets) { + for (int i = 0; i < numTickets; i++) { + LotteryTicket ticket = LotteryTicket.create(getRandomPlayerDetails(), LotteryNumbers.createRandom()); + lotteryService.submitTicket(ticket); + } + } + + private static PlayerDetails getRandomPlayerDetails() { + Random random = new Random(); + int idx = random.nextInt(allPlayerDetails.size()); + return allPlayerDetails.get(idx); + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java new file mode 100644 index 000000000..bc625b230 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java @@ -0,0 +1,53 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.administration; + +import java.util.Map; + +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketId; + +/** + * + * Administrator interface for lottery service. + * + */ +public interface LotteryAdministration { + + /** + * Get all the lottery tickets submitted for lottery + */ + Map getAllSubmittedTickets(); + + /** + * Draw lottery numbers + */ + LotteryNumbers performLottery(); + + /** + * Begin new lottery round + */ + void resetLottery(); + +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java new file mode 100644 index 000000000..a452600aa --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java @@ -0,0 +1,87 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.administration; + +import java.util.Map; + +import com.iluwatar.hexagonal.banking.WireTransfers; +import com.iluwatar.hexagonal.banking.WireTransfersImpl; +import com.iluwatar.hexagonal.database.LotteryTicketRepository; +import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; +import com.iluwatar.hexagonal.domain.LotteryConstants; +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; +import com.iluwatar.hexagonal.domain.LotteryTicketId; +import com.iluwatar.hexagonal.notifications.LotteryNotifications; +import com.iluwatar.hexagonal.notifications.LotteryNotificationsImpl; +import com.iluwatar.hexagonal.service.LotteryService; +import com.iluwatar.hexagonal.service.LotteryServiceImpl; + +/** + * + * Lottery administration implementation + * + */ +public class LotteryAdministrationImpl implements LotteryAdministration { + + private final LotteryTicketRepository repository; + private final LotteryService service = new LotteryServiceImpl(); + private final LotteryNotifications notifications = new LotteryNotificationsImpl(); + private final WireTransfers bank = new WireTransfersImpl(); + public LotteryAdministrationImpl() { + repository = new LotteryTicketInMemoryRepository(); + } + + @Override + public Map getAllSubmittedTickets() { + return repository.findAll(); + } + + @Override + public LotteryNumbers performLottery() { + LotteryNumbers numbers = LotteryNumbers.createRandom(); + Map tickets = getAllSubmittedTickets(); + for (LotteryTicketId id: tickets.keySet()) { + LotteryTicketCheckResult result = service.checkTicketForPrize(id, numbers); + if (result.getResult().equals(CheckResult.WIN_PRIZE)) { + boolean transferred = bank.transferFunds(LotteryConstants.PRIZE_AMOUNT, LotteryConstants.SERVICE_BANK_ACCOUNT, + tickets.get(id).getPlayerDetails().getBankAccount()); + if (transferred) { + notifications.notifyPrize(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); + } else { + notifications.notifyPrizeError(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); + } + } else if (result.getResult().equals(CheckResult.NO_PRIZE)) { + notifications.notifyNoWin(tickets.get(id).getPlayerDetails()); + } + } + return numbers; + } + + @Override + public void resetLottery() { + repository.deleteAll(); + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfers.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfers.java new file mode 100644 index 000000000..7d21e7bf3 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfers.java @@ -0,0 +1,47 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.banking; + +/** + * + * Interface to bank accounts. + * + */ +public interface WireTransfers { + + /** + * Set amount of funds for bank account + */ + void setFunds(String bankAccount, int amount); + + /** + * Get amount of funds for bank account + */ + int getFunds(String bankAccount); + + /** + * Transfer funds from one bank account to another + */ + boolean transferFunds(int amount, String sourceBackAccount, String destinationBankAccount); + +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfersImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfersImpl.java new file mode 100644 index 000000000..19c1bb1c0 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfersImpl.java @@ -0,0 +1,63 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.banking; + +import java.util.HashMap; +import java.util.Map; + +import com.iluwatar.hexagonal.domain.LotteryConstants; + +/** + * + * Banking implementation + * + */ +public class WireTransfersImpl implements WireTransfers { + + private static Map accounts = new HashMap<>(); + + static { + accounts.put(LotteryConstants.SERVICE_BANK_ACCOUNT, LotteryConstants.SERVICE_BANK_ACCOUNT_SALDO); + } + + @Override + public void setFunds(String bankAccount, int amount) { + accounts.put(bankAccount, amount); + } + + @Override + public int getFunds(String bankAccount) { + return accounts.getOrDefault(bankAccount, 0); + } + + @Override + public boolean transferFunds(int amount, String sourceBackAccount, String destinationBankAccount) { + if (accounts.getOrDefault(sourceBackAccount, 0) >= amount) { + accounts.put(sourceBackAccount, accounts.get(sourceBackAccount) - amount); + accounts.put(destinationBankAccount, accounts.get(destinationBankAccount) + amount); + return true; + } else { + return false; + } + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketInMemoryRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketInMemoryRepository.java new file mode 100644 index 000000000..fe17d8cdf --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketInMemoryRepository.java @@ -0,0 +1,67 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.database; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketId; + +/** + * + * Mock database for lottery tickets. + * + */ +public class LotteryTicketInMemoryRepository implements LotteryTicketRepository { + + private static Map tickets = new HashMap<>(); + + @Override + public Optional findById(LotteryTicketId id) { + LotteryTicket ticket = tickets.get(id); + if (ticket == null) { + return Optional.empty(); + } else { + return Optional.of(ticket); + } + } + + @Override + public Optional save(LotteryTicket ticket) { + LotteryTicketId id = new LotteryTicketId(); + tickets.put(id, ticket); + return Optional.of(id); + } + + @Override + public Map findAll() { + return tickets; + } + + @Override + public void deleteAll() { + tickets.clear(); + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepository.java new file mode 100644 index 000000000..4c6522838 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepository.java @@ -0,0 +1,58 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.database; + +import java.util.Map; +import java.util.Optional; + +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketId; + +/** + * + * Interface for accessing lottery tickets in database. + * + */ +public interface LotteryTicketRepository { + + /** + * Find lottery ticket by id + */ + Optional findById(LotteryTicketId id); + + /** + * Save lottery ticket + */ + Optional save(LotteryTicket ticket); + + /** + * Get all lottery tickets + */ + Map findAll(); + + /** + * Delete all lottery tickets + */ + void deleteAll(); + +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryConstants.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryConstants.java new file mode 100644 index 000000000..fb4c8025f --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryConstants.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +/** + * + * Lottery domain constants + * + */ +public class LotteryConstants { + + public static final int PRIZE_AMOUNT = 100000; + public static final String SERVICE_BANK_ACCOUNT = "123-123"; + public static final int TICKET_PRIZE = 3; + public static final int SERVICE_BANK_ACCOUNT_SALDO = 150000; + public static final int PLAYER_MAX_SALDO = 100; + +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java new file mode 100644 index 000000000..6f54e743b --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java @@ -0,0 +1,152 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +import java.util.Collections; +import java.util.HashSet; +import java.util.PrimitiveIterator; +import java.util.Random; +import java.util.Set; + +/** + * + * Value object representing lottery numbers. This lottery uses sets of 4 numbers. The numbers must be unique and + * between 1 and 20. + * + */ +public class LotteryNumbers { + + private final Set numbers; + + public static final int MIN_NUMBER = 1; + public static final int MAX_NUMBER = 20; + public static final int NUM_NUMBERS = 4; + + /** + * Constructor. Creates random lottery numbers. + */ + private LotteryNumbers() { + numbers = new HashSet<>(); + generateRandomNumbers(); + } + + /** + * Constructor. Uses given numbers. + */ + private LotteryNumbers(Set givenNumbers) { + numbers = new HashSet<>(); + numbers.addAll(givenNumbers); + } + + /** + * @return random LotteryNumbers + */ + public static LotteryNumbers createRandom() { + return new LotteryNumbers(); + } + + /** + * @return given LotteryNumbers + */ + public static LotteryNumbers create(Set givenNumbers) { + return new LotteryNumbers(givenNumbers); + } + + /** + * @return lottery numbers + */ + public Set getNumbers() { + return Collections.unmodifiableSet(numbers); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((numbers == null) ? 0 : numbers.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + LotteryNumbers other = (LotteryNumbers) obj; + if (numbers == null) { + if (other.numbers != null) { + return false; + } + } else if (!numbers.equals(other.numbers)) { + return false; + } + return true; + } + + /** + * Generates 4 unique random numbers between 1-20 into numbers set. + */ + private void generateRandomNumbers() { + numbers.clear(); + RandomNumberGenerator generator = new RandomNumberGenerator(MIN_NUMBER, MAX_NUMBER); + while (numbers.size() < NUM_NUMBERS) { + int num = generator.nextInt(); + if (!numbers.contains(num)) { + numbers.add(num); + } + } + } + + /** + * + * Helper class for generating random numbers. + * + */ + private static class RandomNumberGenerator { + + private PrimitiveIterator.OfInt randomIterator; + + /** + * Initialize a new random number generator that generates random numbers in the range [min, max] + * + * @param min the min value (inclusive) + * @param max the max value (inclusive) + */ + public RandomNumberGenerator(int min, int max) { + randomIterator = new Random().ints(min, max + 1).iterator(); + } + + /** + * @return a random number in the range (min, max) + */ + public int nextInt() { + return randomIterator.nextInt(); + } + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java new file mode 100644 index 000000000..e5828cfbf --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java @@ -0,0 +1,101 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +/** + * + * Immutable value object representing lottery ticket. + * + */ +public class LotteryTicket { + + private final PlayerDetails playerDetails; + private final LotteryNumbers lotteryNumbers; + + /** + * Constructor. + */ + private LotteryTicket(PlayerDetails details, LotteryNumbers numbers) { + playerDetails = details; + lotteryNumbers = numbers; + } + + /** + * Factory for creating lottery tickets; + */ + public static LotteryTicket create(PlayerDetails details, LotteryNumbers numbers) { + return new LotteryTicket(details, numbers); + } + + /** + * @return player details + */ + public PlayerDetails getPlayerDetails() { + return playerDetails; + } + + /** + * @return lottery numbers + */ + public LotteryNumbers getNumbers() { + return lotteryNumbers; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((lotteryNumbers == null) ? 0 : lotteryNumbers.hashCode()); + result = prime * result + ((playerDetails == null) ? 0 : playerDetails.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + LotteryTicket other = (LotteryTicket) obj; + if (lotteryNumbers == null) { + if (other.lotteryNumbers != null) { + return false; + } + } else if (!lotteryNumbers.equals(other.lotteryNumbers)) { + return false; + } + if (playerDetails == null) { + if (other.playerDetails != null) { + return false; + } + } else if (!playerDetails.equals(other.playerDetails)) { + return false; + } + return true; + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java new file mode 100644 index 000000000..ded65d270 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java @@ -0,0 +1,96 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +/** + * + * Represents lottery ticket check result. + * + */ +public class LotteryTicketCheckResult { + + public enum CheckResult { WIN_PRIZE, NO_PRIZE, TICKET_NOT_SUBMITTED }; + + private final CheckResult checkResult; + private final int prizeAmount; + + /** + * Constructor. + */ + public LotteryTicketCheckResult(CheckResult result) { + checkResult = result; + prizeAmount = 0; + } + + /** + * Constructor. + */ + public LotteryTicketCheckResult(CheckResult result, int amount) { + checkResult = result; + prizeAmount = amount; + } + + /** + * @return check result + */ + public CheckResult getResult() { + return checkResult; + } + + /** + * @return prize amount + */ + public int getPrizeAmount() { + return prizeAmount; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((checkResult == null) ? 0 : checkResult.hashCode()); + result = prime * result + prizeAmount; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + LotteryTicketCheckResult other = (LotteryTicketCheckResult) obj; + if (checkResult != other.checkResult) { + return false; + } + if (prizeAmount != other.prizeAmount) { + return false; + } + return true; + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java new file mode 100644 index 000000000..710091222 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java @@ -0,0 +1,41 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +import java.util.UUID; + +/** + * Lottery ticked id + */ +public class LotteryTicketId { + + private final UUID id; + + public LotteryTicketId() { + id = UUID.randomUUID(); + } + + public UUID getId() { + return id; + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java new file mode 100644 index 000000000..2fcdb6eb3 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java @@ -0,0 +1,118 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +/** + * + * Immutable value object containing lottery player details. + * + */ +public class PlayerDetails { + + private final String emailAddress; + private final String bankAccountNumber; + private final String phoneNumber; + + /** + * Constructor. + */ + private PlayerDetails(String email, String bankAccount, String phone) { + emailAddress = email; + bankAccountNumber = bankAccount; + phoneNumber = phone; + } + + /** + * Factory for creating new objects. + */ + public static PlayerDetails create(String email, String bankAccount, String phone) { + return new PlayerDetails(email, bankAccount, phone); + } + + /** + * @return email + */ + public String getEmail() { + return emailAddress; + } + + /** + * @return bank account number + */ + public String getBankAccount() { + return bankAccountNumber; + } + + /** + * @return phone number + */ + public String getPhoneNumber() { + return phoneNumber; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((bankAccountNumber == null) ? 0 : bankAccountNumber.hashCode()); + result = prime * result + ((emailAddress == null) ? 0 : emailAddress.hashCode()); + result = prime * result + ((phoneNumber == null) ? 0 : phoneNumber.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + PlayerDetails other = (PlayerDetails) obj; + if (bankAccountNumber == null) { + if (other.bankAccountNumber != null) { + return false; + } + } else if (!bankAccountNumber.equals(other.bankAccountNumber)) { + return false; + } + if (emailAddress == null) { + if (other.emailAddress != null) { + return false; + } + } else if (!emailAddress.equals(other.emailAddress)) { + return false; + } + if (phoneNumber == null) { + if (other.phoneNumber != null) { + return false; + } + } else if (!phoneNumber.equals(other.phoneNumber)) { + return false; + } + return true; + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java new file mode 100644 index 000000000..d7a0cc870 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java @@ -0,0 +1,59 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.notifications; + +import com.iluwatar.hexagonal.domain.PlayerDetails; + +/** + * + * Provides notifications for lottery events. + * + */ +public interface LotteryNotifications { + + /** + * Notify lottery ticket was submitted + */ + void notifyTicketSubmitted(PlayerDetails details); + + /** + * Notify there was an error submitting lottery ticket + */ + void notifyTicketSubmitError(PlayerDetails details); + + /** + * Notify lottery ticket did not win + */ + void notifyNoWin(PlayerDetails details); + + /** + * Notify that prize has been paid + */ + void notifyPrize(PlayerDetails details, int prizeAmount); + + /** + * Notify that there was an error paying the prize + */ + void notifyPrizeError(PlayerDetails details, int prizeAmount); + +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java new file mode 100644 index 000000000..c59a47970 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java @@ -0,0 +1,61 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.notifications; + +import com.iluwatar.hexagonal.domain.PlayerDetails; + +public class LotteryNotificationsImpl implements LotteryNotifications { + + @Override + public void notifyTicketSubmitted(PlayerDetails details) { + System.out.println(String.format("Lottery ticket for %s was submitted. Bank account %s was charged for 3 credits.", + details.getEmail(), details.getBankAccount())); + } + + @Override + public void notifyNoWin(PlayerDetails details) { + System.out.println(String.format("Lottery ticket for %s was checked and unfortunately did not win this time.", + details.getEmail())); + } + + @Override + public void notifyPrize(PlayerDetails details, int prizeAmount) { + System.out + .println(String.format("Lottery ticket for %s has won! The bank account %s was deposited with %d credits.", + details.getEmail(), details.getBankAccount(), prizeAmount)); + } + + @Override + public void notifyPrizeError(PlayerDetails details, int prizeAmount) { + System.out + .println(String.format("Lottery ticket for %s has won! Unfortunately the bank credit transfer of %d failed.", + details.getEmail(), prizeAmount)); + } + + @Override + public void notifyTicketSubmitError(PlayerDetails details) { + System.out.println( + String.format("Lottery ticket for %s could not be submitted because the credit transfer of 3 credits failed.", + details.getEmail())); + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java new file mode 100644 index 000000000..0056e794b --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java @@ -0,0 +1,48 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.service; + +import java.util.Optional; + +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; +import com.iluwatar.hexagonal.domain.LotteryTicketId; + +/** + * + * Interface for submitting and checking lottery tickets. + * + */ +public interface LotteryService { + + /** + * Submit lottery ticket to participate in the lottery + */ + Optional submitTicket(LotteryTicket ticket); + + /** + * Check if lottery ticket has won + */ + LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers); +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java new file mode 100644 index 000000000..58df1c7c8 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java @@ -0,0 +1,88 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.service; + +import java.util.Optional; + +import com.iluwatar.hexagonal.banking.WireTransfers; +import com.iluwatar.hexagonal.banking.WireTransfersImpl; +import com.iluwatar.hexagonal.database.LotteryTicketRepository; +import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; +import com.iluwatar.hexagonal.domain.LotteryConstants; +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; +import com.iluwatar.hexagonal.domain.LotteryTicketId; +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; +import com.iluwatar.hexagonal.notifications.LotteryNotifications; +import com.iluwatar.hexagonal.notifications.LotteryNotificationsImpl; + +/** + * + * Implementation for lottery service + * + */ +public class LotteryServiceImpl implements LotteryService { + + private final LotteryTicketRepository repository; + + private final WireTransfers bank = new WireTransfersImpl(); + + private final LotteryNotifications notifications = new LotteryNotificationsImpl(); + + /** + * Constructor + */ + public LotteryServiceImpl() { + repository = new LotteryTicketInMemoryRepository(); + } + + @Override + public Optional submitTicket(LotteryTicket ticket) { + boolean result = bank.transferFunds(LotteryConstants.TICKET_PRIZE, ticket.getPlayerDetails().getBankAccount(), + LotteryConstants.SERVICE_BANK_ACCOUNT); + if (result == false) { + notifications.notifyTicketSubmitError(ticket.getPlayerDetails()); + return Optional.empty(); + } + Optional optional = repository.save(ticket); + if (optional.isPresent()) { + notifications.notifyTicketSubmitted(ticket.getPlayerDetails()); + } + return optional; + } + + @Override + public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { + Optional optional = repository.findById(id); + if (optional.isPresent()) { + if (optional.get().getNumbers().equals(winningNumbers)) { + return new LotteryTicketCheckResult(CheckResult.WIN_PRIZE, 1000); + } else { + return new LotteryTicketCheckResult(CheckResult.NO_PRIZE); + } + } else { + return new LotteryTicketCheckResult(CheckResult.TICKET_NOT_SUBMITTED); + } + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java new file mode 100644 index 000000000..8e19cca35 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal; + +import org.junit.Test; + +/** + * Unit test for simple App. + */ +public class AppTest { + + @Test + public void testApp() { + String[] args = {}; + App.main(args); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/WireTransfersTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/WireTransfersTest.java new file mode 100644 index 000000000..0274feca3 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/WireTransfersTest.java @@ -0,0 +1,50 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.banking; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * + * Tests for banking + * + */ +public class WireTransfersTest { + + private final WireTransfers bank = new WireTransfersImpl(); + + @Test + public void testInit() { + assertEquals(bank.getFunds("foo"), 0); + bank.setFunds("foo", 100); + assertEquals(bank.getFunds("foo"), 100); + bank.setFunds("bar", 150); + assertEquals(bank.getFunds("bar"), 150); + assertTrue(bank.transferFunds(50, "bar", "foo")); + assertEquals(bank.getFunds("foo"), 150); + assertEquals(bank.getFunds("bar"), 100); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java new file mode 100644 index 000000000..b20e928c8 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java @@ -0,0 +1,64 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.database; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Optional; + +import org.junit.Before; +import org.junit.Test; + +import com.iluwatar.hexagonal.database.LotteryTicketRepository; +import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketId; +import com.iluwatar.hexagonal.test.LotteryTestUtils; + +/** + * + * Tests for {@link LotteryTicketRepository} + * + */ +public class LotteryTicketRepositoryTest { + + private final LotteryTicketRepository repository = new LotteryTicketInMemoryRepository(); + + @Before + public void clear() { + repository.deleteAll(); + } + + @Test + public void testCrudOperations() { + LotteryTicketRepository repository = new LotteryTicketInMemoryRepository(); + assertEquals(repository.findAll().size(), 0); + LotteryTicket ticket = LotteryTestUtils.createLotteryTicket(); + Optional id = repository.save(ticket); + assertTrue(id.isPresent()); + assertEquals(repository.findAll().size(), 1); + Optional optionalTicket = repository.findById(id.get()); + assertTrue(optionalTicket.isPresent()); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryNumbersTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryNumbersTest.java new file mode 100644 index 000000000..4f2751167 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryNumbersTest.java @@ -0,0 +1,76 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.HashSet; + +import org.junit.Test; + +/** + * + * Unit tests for {@link LotteryNumbers} + * + */ +public class LotteryNumbersTest { + + @Test + public void testGivenNumbers() { + LotteryNumbers numbers = LotteryNumbers.create( + new HashSet<>(Arrays.asList(1, 2, 3, 4))); + assertEquals(numbers.getNumbers().size(), 4); + assertTrue(numbers.getNumbers().contains(1)); + assertTrue(numbers.getNumbers().contains(2)); + assertTrue(numbers.getNumbers().contains(3)); + assertTrue(numbers.getNumbers().contains(4)); + } + + @Test(expected = UnsupportedOperationException.class) + public void testNumbersCantBeModified() { + LotteryNumbers numbers = LotteryNumbers.create( + new HashSet<>(Arrays.asList(1, 2, 3, 4))); + numbers.getNumbers().add(5); + } + + @Test + public void testRandomNumbers() { + LotteryNumbers numbers = LotteryNumbers.createRandom(); + assertEquals(numbers.getNumbers().size(), LotteryNumbers.NUM_NUMBERS); + } + + @Test + public void testEquals() { + LotteryNumbers numbers1 = LotteryNumbers.create( + new HashSet<>(Arrays.asList(1, 2, 3, 4))); + LotteryNumbers numbers2 = LotteryNumbers.create( + new HashSet<>(Arrays.asList(1, 2, 3, 4))); + assertTrue(numbers1.equals(numbers2)); + LotteryNumbers numbers3 = LotteryNumbers.create( + new HashSet<>(Arrays.asList(11, 12, 13, 14))); + assertFalse(numbers1.equals(numbers3)); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResultTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResultTest.java new file mode 100644 index 000000000..237ab85d3 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResultTest.java @@ -0,0 +1,47 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import org.junit.Test; + +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; + +/** + * + * Unit tests for {@link LotteryTicketCheckResult} + * + */ +public class LotteryTicketCheckResultTest { + + @Test + public void testEquals() { + LotteryTicketCheckResult result1 = new LotteryTicketCheckResult(CheckResult.NO_PRIZE); + LotteryTicketCheckResult result2 = new LotteryTicketCheckResult(CheckResult.NO_PRIZE); + assertEquals(result1, result2); + LotteryTicketCheckResult result3 = new LotteryTicketCheckResult(CheckResult.WIN_PRIZE, 300000); + assertFalse(result1.equals(result3)); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java new file mode 100644 index 000000000..e1918686a --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java @@ -0,0 +1,49 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import java.util.Arrays; +import java.util.HashSet; + +import org.junit.Test; + +public class LotteryTicketTest { + + @Test + public void testEquals() { + PlayerDetails details1 = PlayerDetails.create("bob@foo.bar", "1212-121212", "+34332322"); + LotteryNumbers numbers1 = LotteryNumbers.create(new HashSet(Arrays.asList(1, 2, 3, 4))); + LotteryTicket ticket1 = LotteryTicket.create(details1, numbers1); + PlayerDetails details2 = PlayerDetails.create("bob@foo.bar", "1212-121212", "+34332322"); + LotteryNumbers numbers2 = LotteryNumbers.create(new HashSet(Arrays.asList(1, 2, 3, 4))); + LotteryTicket ticket2 = LotteryTicket.create(details2, numbers2); + assertEquals(ticket1, ticket2); + PlayerDetails details3 = PlayerDetails.create("elsa@foo.bar", "1223-121212", "+49332322"); + LotteryNumbers numbers3 = LotteryNumbers.create(new HashSet(Arrays.asList(1, 2, 3, 8))); + LotteryTicket ticket3 = LotteryTicket.create(details3, numbers3); + assertFalse(ticket1.equals(ticket3)); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java new file mode 100644 index 000000000..813b035a2 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java @@ -0,0 +1,45 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import org.junit.Test; + +/** + * + * Unit tests for {@link PlayerDetails} + * + */ +public class PlayerDetailsTest { + + @Test + public void testEquals() { + PlayerDetails details1 = PlayerDetails.create("tom@foo.bar", "11212-123434", "+12323425"); + PlayerDetails details2 = PlayerDetails.create("tom@foo.bar", "11212-123434", "+12323425"); + assertEquals(details1, details2); + PlayerDetails details3 = PlayerDetails.create("john@foo.bar", "16412-123439", "+34323432"); + assertFalse(details1.equals(details3)); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java new file mode 100644 index 000000000..27e5bb6e4 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java @@ -0,0 +1,116 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.lottery; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; + +import org.junit.Before; +import org.junit.Test; + +import com.iluwatar.hexagonal.administration.LotteryAdministration; +import com.iluwatar.hexagonal.administration.LotteryAdministrationImpl; +import com.iluwatar.hexagonal.banking.WireTransfers; +import com.iluwatar.hexagonal.banking.WireTransfersImpl; +import com.iluwatar.hexagonal.database.LotteryTicketRepository; +import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; +import com.iluwatar.hexagonal.domain.LotteryTicketId; +import com.iluwatar.hexagonal.service.LotteryService; +import com.iluwatar.hexagonal.service.LotteryServiceImpl; +import com.iluwatar.hexagonal.test.LotteryTestUtils; + +/** + * + * Test the lottery system + * + */ +public class LotteryTest { + + private final LotteryAdministration admin = new LotteryAdministrationImpl(); + private final LotteryService service = new LotteryServiceImpl(); + private final LotteryTicketRepository repository = new LotteryTicketInMemoryRepository(); + private final WireTransfers wireTransfers = new WireTransfersImpl(); + + @Before + public void clear() { + repository.deleteAll(); + } + + @Test + public void testLottery() { + + // setup bank account with funds + wireTransfers.setFunds("123-12312", 100); + + // admin resets the lottery + admin.resetLottery(); + assertEquals(admin.getAllSubmittedTickets().size(), 0); + + // players submit the lottery tickets + Optional ticket1 = service.submitTicket(LotteryTestUtils.createLotteryTicket("cvt@bbb.com", + "123-12312", "+32425255", new HashSet<>(Arrays.asList(1, 2, 3, 4)))); + assertTrue(ticket1.isPresent()); + Optional ticket2 = service.submitTicket(LotteryTestUtils.createLotteryTicket("ant@bac.com", + "123-12312", "+32423455", new HashSet<>(Arrays.asList(11, 12, 13, 14)))); + assertTrue(ticket2.isPresent()); + Optional ticket3 = service.submitTicket(LotteryTestUtils.createLotteryTicket("arg@boo.com", + "123-12312", "+32421255", new HashSet<>(Arrays.asList(6, 8, 13, 19)))); + assertTrue(ticket3.isPresent()); + assertEquals(admin.getAllSubmittedTickets().size(), 3); + + // perform lottery + LotteryNumbers winningNumbers = admin.performLottery(); + + // cheat a bit for testing sake, use winning numbers to submit another ticket + Optional ticket4 = service.submitTicket(LotteryTestUtils.createLotteryTicket("lucky@orb.com", + "123-12312", "+12421255", winningNumbers.getNumbers())); + assertTrue(ticket4.isPresent()); + assertEquals(admin.getAllSubmittedTickets().size(), 4); + + // check winners + Map tickets = admin.getAllSubmittedTickets(); + for (LotteryTicketId id: tickets.keySet()) { + LotteryTicketCheckResult checkResult = service.checkTicketForPrize(id, winningNumbers); + assertTrue(checkResult.getResult() != CheckResult.TICKET_NOT_SUBMITTED); + if (checkResult.getResult().equals(CheckResult.WIN_PRIZE)) { + assertTrue(checkResult.getPrizeAmount() > 0); + } else if (checkResult.getResult().equals(CheckResult.WIN_PRIZE)) { + assertEquals(checkResult.getPrizeAmount(), 0); + } + } + + // check another ticket that has not been submitted + LotteryTicketCheckResult checkResult = service.checkTicketForPrize(new LotteryTicketId(), winningNumbers); + assertTrue(checkResult.getResult() == CheckResult.TICKET_NOT_SUBMITTED); + assertEquals(checkResult.getPrizeAmount(), 0); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java new file mode 100644 index 000000000..883c8127f --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java @@ -0,0 +1,56 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.test; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.PlayerDetails; + +/** + * + * Utilities for lottery tests + * + */ +public class LotteryTestUtils { + + /** + * @return lottery ticket + */ + public static LotteryTicket createLotteryTicket() { + return createLotteryTicket("foo@bar.com", "12231-213132", "+99324554", new HashSet<>(Arrays.asList(1, 2, 3, 4))); + } + + /** + * @return lottery ticket + */ + public static LotteryTicket createLotteryTicket(String email, String account, String phone, + Set givenNumbers) { + PlayerDetails details = PlayerDetails.create(email, account, phone); + LotteryNumbers numbers = LotteryNumbers.create(givenNumbers); + return LotteryTicket.create(details, numbers); + } +} diff --git a/intercepting-filter/README.md b/intercepting-filter/README.md new file mode 100644 index 000000000..7d53472a0 --- /dev/null +++ b/intercepting-filter/README.md @@ -0,0 +1,33 @@ +--- +layout: pattern +title: Intercepting Filter +folder: intercepting-filter +permalink: /patterns/intercepting-filter/ +categories: Behavioral +tags: + - Java + - Difficulty-Intermediate +--- + +## Intent +Provide pluggable filters to conduct necessary pre-processing and +post-processing to requests from a client to a target + +![alt text](./etc/intercepting-filter.png "Intercepting Filter") + +## Applicability +Use the Intercepting Filter pattern when + +* a system uses pre-processing or post-processing requests +* a system should do the authentication/ authorization/ logging or tracking of request and then pass the requests to corresponding handlers +* you want a modular approach to configuring pre-processing and post-processing schemes + +## Real world examples + +* [javax.servlet.FilterChain](https://tomcat.apache.org/tomcat-8.0-doc/servletapi/javax/servlet/FilterChain.html) and [javax.servlet.Filter](https://tomcat.apache.org/tomcat-8.0-doc/servletapi/javax/servlet/Filter.html) +* [Struts 2 - Interceptors](https://struts.apache.org/docs/interceptors.html) + +## Credits + +* [TutorialsPoint - Intercepting Filter](http://www.tutorialspoint.com/design_pattern/intercepting_filter_pattern.htm) +* [Presentation Tier Patterns](http://www.javagyan.com/tutorials/corej2eepatterns/presentation-tier-patterns) diff --git a/intercepting-filter/etc/intercepting-filter.ucls b/intercepting-filter/etc/intercepting-filter.ucls index c778f536e..9cf8d3c00 100644 --- a/intercepting-filter/etc/intercepting-filter.ucls +++ b/intercepting-filter/etc/intercepting-filter.ucls @@ -1,8 +1,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/intercepting-filter/pom.xml b/intercepting-filter/pom.xml index ae450f8ec..6bb6f95cc 100644 --- a/intercepting-filter/pom.xml +++ b/intercepting-filter/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT intercepting-filter @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/AbstractFilter.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/AbstractFilter.java new file mode 100644 index 000000000..e81531297 --- /dev/null +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/AbstractFilter.java @@ -0,0 +1,66 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.intercepting.filter; + +/** + * Base class for order processing filters. Handles chain management. + * + */ +public abstract class AbstractFilter implements Filter { + + private Filter next; + + public AbstractFilter() {} + + public AbstractFilter(Filter next) { + this.next = next; + } + + @Override + public void setNext(Filter filter) { + this.next = filter; + } + + @Override + public Filter getNext() { + return next; + } + + @Override + public Filter getLast() { + Filter last = this; + while (last.getNext() != null) { + last = last.getNext(); + } + return last; + } + + @Override + public String execute(Order order) { + if (getNext() != null) { + return getNext().execute(order); + } else { + return ""; + } + } +} diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/AddressFilter.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/AddressFilter.java new file mode 100644 index 000000000..5b6ff4155 --- /dev/null +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/AddressFilter.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.intercepting.filter; + +/** + * Concrete implementation of filter This filter is responsible for checking/filtering the input in + * the address field. + * + * @author joshzambales + * + */ +public class AddressFilter extends AbstractFilter { + + @Override + public String execute(Order order) { + String result = super.execute(order); + if (order.getAddress() == null || order.getAddress().isEmpty()) { + return result + "Invalid address! "; + } else { + return result; + } + } +} diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/App.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/App.java new file mode 100644 index 000000000..9d16737d0 --- /dev/null +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/App.java @@ -0,0 +1,69 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.intercepting.filter; + +/** + * + * When a request enters a Web application, it often must pass several entrance tests prior to the + * main processing stage. For example, - Has the client been authenticated? - Does the client have a + * valid session? - Is the client's IP address from a trusted network? - Does the request path + * violate any constraints? - What encoding does the client use to send the data? - Do we support + * the browser type of the client? Some of these checks are tests, resulting in a yes or no answer + * that determines whether processing will continue. Other checks manipulate the incoming data + * stream into a form suitable for processing. + *

+ * The classic solution consists of a series of conditional checks, with any failed check aborting + * the request. Nested if/else statements are a standard strategy, but this solution leads to code + * fragility and a copy-and-paste style of programming, because the flow of the filtering and the + * action of the filters is compiled into the application. + *

+ * The key to solving this problem in a flexible and unobtrusive manner is to have a simple + * mechanism for adding and removing processing components, in which each component completes a + * specific filtering action. This is the Intercepting Filter pattern in action. + *

+ * In this example we check whether the order request is valid through pre-processing done via + * {@link Filter}. Each field has its own corresponding {@link Filter} + *

+ * + * @author joshzambales + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + FilterManager filterManager = new FilterManager(); + filterManager.addFilter(new NameFilter()); + filterManager.addFilter(new ContactFilter()); + filterManager.addFilter(new AddressFilter()); + filterManager.addFilter(new DepositFilter()); + filterManager.addFilter(new OrderFilter()); + + Client client = new Client(); + client.setFilterManager(filterManager); + } +} diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Client.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Client.java new file mode 100644 index 000000000..f2f1a6fbc --- /dev/null +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Client.java @@ -0,0 +1,135 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.intercepting.filter; + +import java.awt.BorderLayout; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRootPane; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; + +/** + * The Client class is responsible for handling the input and running them through filters inside + * the {@link FilterManager}. + * + * This is where {@link Filter}s come to play as the client pre-processes the request before being + * displayed in the {@link Target}. + * + * @author joshzambales + * + */ +public class Client extends JFrame { + + private static final long serialVersionUID = 1L; + + private FilterManager filterManager; + private JLabel jl; + private JTextField[] jtFields; + private JTextArea[] jtAreas; + private JButton clearButton; + private JButton processButton; + + /** + * Constructor + */ + public Client() { + super("Client System"); + setDefaultCloseOperation(EXIT_ON_CLOSE); + setSize(300, 300); + jl = new JLabel("RUNNING..."); + jtFields = new JTextField[3]; + for (int i = 0; i < 3; i++) { + jtFields[i] = new JTextField(); + } + jtAreas = new JTextArea[2]; + for (int i = 0; i < 2; i++) { + jtAreas[i] = new JTextArea(); + } + clearButton = new JButton("Clear"); + processButton = new JButton("Process"); + + setup(); + } + + private void setup() { + setLayout(new BorderLayout()); + JPanel panel = new JPanel(); + add(jl, BorderLayout.SOUTH); + add(panel, BorderLayout.CENTER); + panel.setLayout(new GridLayout(6, 2)); + panel.add(new JLabel("Name")); + panel.add(jtFields[0]); + panel.add(new JLabel("Contact Number")); + panel.add(jtFields[1]); + panel.add(new JLabel("Address")); + panel.add(jtAreas[0]); + panel.add(new JLabel("Deposit Number")); + panel.add(jtFields[2]); + panel.add(new JLabel("Order")); + panel.add(jtAreas[1]); + panel.add(clearButton); + panel.add(processButton); + + clearButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + for (JTextArea i : jtAreas) { + i.setText(""); + } + for (JTextField i : jtFields) { + i.setText(""); + } + } + }); + + processButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + Order order = + new Order(jtFields[0].getText(), jtFields[1].getText(), jtAreas[0].getText(), + jtFields[2].getText(), jtAreas[1].getText()); + jl.setText(sendRequest(order)); + } + }); + + JRootPane rootPane = SwingUtilities.getRootPane(processButton); + rootPane.setDefaultButton(processButton); + setVisible(true); + } + + public void setFilterManager(FilterManager filterManager) { + this.filterManager = filterManager; + } + + public String sendRequest(Order order) { + return filterManager.filterRequest(order); + } +} diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/ContactFilter.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/ContactFilter.java new file mode 100644 index 000000000..098de178b --- /dev/null +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/ContactFilter.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.intercepting.filter; + +/** + * Concrete implementation of filter This filter checks for the contact field in which it checks if + * the input consist of numbers and it also checks if the input follows the length constraint (11 + * digits) + * + * @author joshzambales + * + */ +public class ContactFilter extends AbstractFilter { + + @Override + public String execute(Order order) { + String result = super.execute(order); + if (order.getContactNumber() == null || order.getContactNumber().isEmpty() + || order.getContactNumber().matches(".*[^\\d]+.*") + || order.getContactNumber().length() != 11) { + return result + "Invalid contact number! "; + } else { + return result; + } + } +} diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/DepositFilter.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/DepositFilter.java new file mode 100644 index 000000000..08285f79d --- /dev/null +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/DepositFilter.java @@ -0,0 +1,42 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.intercepting.filter; + +/** + * Concrete implementation of filter This checks for the deposit code + * + * @author joshzambales + * + */ +public class DepositFilter extends AbstractFilter { + + @Override + public String execute(Order order) { + String result = super.execute(order); + if (order.getDepositNumber() == null || order.getDepositNumber().isEmpty()) { + return result + "Invalid deposit number! "; + } else { + return result; + } + } +} diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Filter.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Filter.java new file mode 100644 index 000000000..9979df0e7 --- /dev/null +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Filter.java @@ -0,0 +1,53 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.intercepting.filter; + +/** + * Filters perform certain tasks prior or after execution of request by request handler. In this + * case, before the request is handled by the target, the request undergoes through each Filter + * + * @author joshzambales + * + */ +public interface Filter { + + /** + * Execute order processing filter. + */ + String execute(Order order); + + /** + * Set next filter in chain after this. + */ + void setNext(Filter filter); + + /** + * Get next filter in chain after this. + */ + Filter getNext(); + + /** + * Get last filter in the chain. + */ + Filter getLast(); +} diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/FilterChain.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/FilterChain.java new file mode 100644 index 000000000..7c5d74b4c --- /dev/null +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/FilterChain.java @@ -0,0 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.intercepting.filter; + + +/** + * Filter Chain carries multiple filters and help to execute them in defined order on target. + * + * @author joshzambales + */ +public class FilterChain { + + private Filter chain; + + /** + * Constructor + */ + public FilterChain() { + } + + /** + * Adds filter + */ + public void addFilter(Filter filter) { + if (chain == null) { + chain = filter; + } else { + chain.getLast().setNext(filter); + } + } + + /** + * Execute filter chain + */ + public String execute(Order order) { + if (chain != null) { + return chain.execute(order); + } else { + return "RUNNING..."; + } + } +} diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/FilterManager.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/FilterManager.java new file mode 100644 index 000000000..b0e125522 --- /dev/null +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/FilterManager.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.intercepting.filter; + +/** + * Filter Manager manages the filters and {@link FilterChain}. + * + * @author joshzambales + * + */ +public class FilterManager { + + private FilterChain filterChain; + + public FilterManager() { + filterChain = new FilterChain(); + } + + public void addFilter(Filter filter) { + filterChain.addFilter(filter); + } + + public String filterRequest(Order order) { + return filterChain.execute(order); + } +} diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/NameFilter.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/NameFilter.java new file mode 100644 index 000000000..ea8d17413 --- /dev/null +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/NameFilter.java @@ -0,0 +1,44 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.intercepting.filter; + +/** + * Concrete implementation of filter. This filter checks if the input in the Name field is valid. + * (alphanumeric) + * + * @author joshzambales + * + */ +public class NameFilter extends AbstractFilter { + + @Override + public String execute(Order order) { + String result = super.execute(order); + if (order.getName() == null || order.getName().isEmpty() + || order.getName().matches(".*[^\\w|\\s]+.*")) { + return result + "Invalid name! "; + } else { + return result; + } + } +} diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Order.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Order.java new file mode 100644 index 000000000..8a60bed9d --- /dev/null +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Order.java @@ -0,0 +1,89 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.intercepting.filter; + +/** + * Order class carries the order data. + * + */ +public class Order { + + private String name; + private String contactNumber; + private String address; + private String depositNumber; + private String order; + + public Order() {} + + /** + * Constructor + */ + public Order(String name, String contactNumber, String address, String depositNumber, String order) { + this.name = name; + this.contactNumber = contactNumber; + this.address = address; + this.depositNumber = depositNumber; + this.order = order; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getContactNumber() { + return contactNumber; + } + + public void setContactNumber(String contactNumber) { + this.contactNumber = contactNumber; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getDepositNumber() { + return depositNumber; + } + + public void setDepositNumber(String depositNumber) { + this.depositNumber = depositNumber; + } + + public String getOrder() { + return order; + } + + public void setOrder(String order) { + this.order = order; + } +} diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/OrderFilter.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/OrderFilter.java new file mode 100644 index 000000000..9bfe890f3 --- /dev/null +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/OrderFilter.java @@ -0,0 +1,42 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.intercepting.filter; + +/** + * Concrete implementation of filter. This checks for the order field. + * + * @author joshzambales + * + */ +public class OrderFilter extends AbstractFilter { + + @Override + public String execute(Order order) { + String result = super.execute(order); + if (order.getOrder() == null || order.getOrder().isEmpty()) { + return result + "Invalid order! "; + } else { + return result; + } + } +} diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Target.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Target.java new file mode 100644 index 000000000..e8fc693bc --- /dev/null +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Target.java @@ -0,0 +1,104 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.intercepting.filter; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JRootPane; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.SwingUtilities; +import javax.swing.table.DefaultTableModel; + +/** + * This is where the requests are displayed after being validated by filters. + * + * @author mjoshzambales + * + */ +public class Target extends JFrame { + + private static final long serialVersionUID = 1L; + + private JTable jt; + private JScrollPane jsp; + private DefaultTableModel dtm; + private JButton del; + + /** + * Constructor + */ + public Target() { + super("Order System"); + setDefaultCloseOperation(EXIT_ON_CLOSE); + setSize(640, 480); + dtm = + new DefaultTableModel(new Object[] {"Name", "Contact Number", "Address", "Deposit Number", + "Order"}, 0); + jt = new JTable(dtm); + del = new JButton("Delete"); + setup(); + } + + private void setup() { + setLayout(new BorderLayout()); + JPanel bot = new JPanel(); + add(jt.getTableHeader(), BorderLayout.NORTH); + bot.setLayout(new BorderLayout()); + bot.add(del, BorderLayout.EAST); + add(bot, BorderLayout.SOUTH); + jsp = new JScrollPane(jt); + jsp.setPreferredSize(new Dimension(500, 250)); + add(jsp, BorderLayout.CENTER); + + del.addActionListener(new DListener()); + + JRootPane rootPane = SwingUtilities.getRootPane(del); + rootPane.setDefaultButton(del); + setVisible(true); + } + + public void execute(String[] request) { + dtm.addRow(new Object[] {request[0], request[1], request[2], request[3], request[4]}); + } + + class DListener implements ActionListener { + @Override + public void actionPerformed(ActionEvent e) { + int temp = jt.getSelectedRow(); + if (temp == -1) { + return; + } + int temp2 = jt.getSelectedRowCount(); + for (int i = 0; i < temp2; i++) { + dtm.removeRow(temp); + } + } + } +} diff --git a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/AbstractFilter.java b/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/AbstractFilter.java deleted file mode 100644 index 680738d6d..000000000 --- a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/AbstractFilter.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.iluwatar.interceptingfilter; - -/** - * Base class for order processing filters. - * Handles chain management. - * - */ -public abstract class AbstractFilter implements Filter { - - private Filter next; - - public AbstractFilter() { - } - - public AbstractFilter(Filter next) { - this.next = next; - } - - @Override - public void setNext(Filter filter) { - this.next = filter; - } - - @Override - public Filter getNext() { - return next; - } - - @Override - public Filter getLast() { - Filter last = this; - while (last.getNext() != null) { - last = last.getNext(); - } - return last; - } - - @Override - public String execute(Order order) { - if (getNext() != null) { - return getNext().execute(order); - } else { - return ""; - } - } -} diff --git a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/AddressFilter.java b/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/AddressFilter.java deleted file mode 100644 index 836cc3928..000000000 --- a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/AddressFilter.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.iluwatar.interceptingfilter; - -/** - * Concrete implementation of filter - * This filter is responsible for checking/filtering the input in the address field. - * @author joshzambales - * - */ -public class AddressFilter extends AbstractFilter { - - @Override - public String execute(Order order) { - String result = super.execute(order); - if (order.getAddress() == null || order.getAddress().isEmpty()) { - return result + "Invalid address! "; - } else - return result; - } -} diff --git a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/App.java b/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/App.java deleted file mode 100644 index 51e363663..000000000 --- a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/App.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.iluwatar.interceptingfilter; - -/** - * - * This is an app that checks whether the order request is valid through pre-processing done via Filters - * Each field has its own corresponding Filter - * @author joshzambales - * - */ -public class App{ - - public static void main(String[] args) { - FilterManager filterManager = new FilterManager(new Target()); - filterManager.addFilter(new NameFilter()); - filterManager.addFilter(new ContactFilter()); - filterManager.addFilter(new AddressFilter()); - filterManager.addFilter(new DepositFilter()); - filterManager.addFilter(new OrderFilter()); - - Client client = new Client(); - client.setFilterManager(filterManager); - } -} diff --git a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/Client.java b/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/Client.java deleted file mode 100644 index fde2b012d..000000000 --- a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/Client.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.iluwatar.interceptingfilter; - -import java.awt.BorderLayout; -import java.awt.GridLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JRootPane; -import javax.swing.JTextArea; -import javax.swing.JTextField; -import javax.swing.SwingUtilities; - -/** - * The Client class is responsible for handling the input and running them through filters inside the filterManager - * - * This is where Filters come to play as the client pre-processes the request before being displayed in the Target - * - * @author joshzambales - * - */ -public class Client extends JFrame { - - private static final long serialVersionUID = 1L; - - private FilterManager filterManager; - private JLabel jl; - private JTextField[] jtFields; - private JTextArea[] jtAreas; - private JButton clearButton, processButton; - - public Client() { - super("Client System"); - setDefaultCloseOperation(EXIT_ON_CLOSE); - setSize(300, 300); - jl = new JLabel("RUNNING..."); - jtFields = new JTextField[3]; - for (int i = 0; i < 3; i++) { - jtFields[i] = new JTextField(); - } - jtAreas = new JTextArea[2]; - for (int i = 0; i < 2; i++) { - jtAreas[i] = new JTextArea(); - } - clearButton = new JButton("Clear"); - processButton = new JButton("Process"); - - setup(); - } - - private void setup() { - setLayout(new BorderLayout()); - JPanel panel = new JPanel(); - add(jl, BorderLayout.SOUTH); - add(panel, BorderLayout.CENTER); - panel.setLayout(new GridLayout(6, 2)); - panel.add(new JLabel("Name")); - panel.add(jtFields[0]); - panel.add(new JLabel("Contact Number")); - panel.add(jtFields[1]); - panel.add(new JLabel("Address")); - panel.add(jtAreas[0]); - panel.add(new JLabel("Deposit Number")); - panel.add(jtFields[2]); - panel.add(new JLabel("Order")); - panel.add(jtAreas[1]); - panel.add(clearButton); - panel.add(processButton); - - clearButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - for (JTextArea i : jtAreas) { - i.setText(""); - } - for (JTextField i : jtFields) { - i.setText(""); - } - } - }); - - processButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - Order order = new Order(jtFields[0].getText(), jtFields[1] - .getText(), jtAreas[0].getText(), - jtFields[2].getText(), jtAreas[1].getText()); - jl.setText(sendRequest(order)); - } - }); - - JRootPane rootPane = SwingUtilities.getRootPane(processButton); - rootPane.setDefaultButton(processButton); - setVisible(true); - } - - public void setFilterManager(FilterManager filterManager) { - this.filterManager = filterManager; - } - - public String sendRequest(Order order) { - return filterManager.filterRequest(order); - } -} diff --git a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/ContactFilter.java b/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/ContactFilter.java deleted file mode 100644 index 449d00be1..000000000 --- a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/ContactFilter.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.iluwatar.interceptingfilter; - -/** - * Concrete implementation of filter - * This filter checks for the contact field in which it checks if the input consist of numbers - * and it also checks if the input follows the length constraint (11 digits) - * @author joshzambales - * - */ -public class ContactFilter extends AbstractFilter { - - @Override - public String execute(Order order) { - String result = super.execute(order); - if (order.getContactNumber() == null - || order.getContactNumber().isEmpty() - || order.getContactNumber().matches(".*[^\\d]+.*") - || order.getContactNumber().length() != 11) { - return result + "Invalid contact number! "; - } else { - return result; - } - } -} diff --git a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/DepositFilter.java b/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/DepositFilter.java deleted file mode 100644 index aa46c149c..000000000 --- a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/DepositFilter.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.iluwatar.interceptingfilter; - -/** - * Concrete implementation of filter - * This checks for the deposit code - * @author joshzambales - * - */ -public class DepositFilter extends AbstractFilter { - - @Override - public String execute(Order order) { - String result = super.execute(order); - if (order.getDepositNumber() == null || order.getDepositNumber().isEmpty()) { - return result + "Invalid deposit number! "; - } else { - return result; - } - } -} diff --git a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/Filter.java b/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/Filter.java deleted file mode 100644 index dc7e3445a..000000000 --- a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/Filter.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.iluwatar.interceptingfilter; - -/** - * Filter interface Filters perform certain tasks prior or after execution of - * request by request handler. In this case, before the request is handled by - * the target, the request undergoes through each Filter - * - * @author joshzambales - * - */ -public interface Filter { - - /** - * Execute order processing filter. - * @param order - * @return empty string on success, otherwise error message. - */ - String execute(Order order); - - /** - * Set next filter in chain after this. - * @param filter - */ - void setNext(Filter filter); - - /** - * Get next filter in chain after this. - * @return - */ - Filter getNext(); - - /** - * Get last filter in the chain. - * @return - */ - Filter getLast(); -} diff --git a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/FilterChain.java b/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/FilterChain.java deleted file mode 100644 index 56446833d..000000000 --- a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/FilterChain.java +++ /dev/null @@ -1,34 +0,0 @@ - package com.iluwatar.interceptingfilter; - - -/** - * Filter Chain carries multiple filters and help to execute them in defined order on target. - * - * @author joshzambales - */ -public class FilterChain { - - private Filter chain; - - private final Target target; - - public FilterChain(Target target) { - this.target = target; - } - - public void addFilter(Filter filter) { - if (chain == null) { - chain = filter; - } else { - chain.getLast().setNext(filter); - } - } - - public String execute(Order order) { - if (chain != null) { - return chain.execute(order); - } else { - return "RUNNING..."; - } - } -} diff --git a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/FilterManager.java b/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/FilterManager.java deleted file mode 100644 index f729ee734..000000000 --- a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/FilterManager.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.iluwatar.interceptingfilter; - -/** - * Filter Manager manages the filters and Filter Chain. - * - * @author joshzambales - * - */ -public class FilterManager { - - private FilterChain filterChain; - - public FilterManager(Target target) { - filterChain = new FilterChain(target); - } - - public void addFilter(Filter filter) { - filterChain.addFilter(filter); - } - - public String filterRequest(Order order) { - return filterChain.execute(order); - } -} diff --git a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/NameFilter.java b/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/NameFilter.java deleted file mode 100644 index ff4c9fd8d..000000000 --- a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/NameFilter.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.iluwatar.interceptingfilter; - -/** - * Concrete implementation of filter This filter checks if the input in the Name - * field is valid. (alphanumeric) - * - * @author joshzambales - * - */ -public class NameFilter extends AbstractFilter { - - @Override - public String execute(Order order) { - String result = super.execute(order); - if (order.getName() == null || order.getName().isEmpty() || order.getName().matches(".*[^\\w|\\s]+.*")) { - return result + "Invalid order! "; - } else { - return result; - } - } -} diff --git a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/Order.java b/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/Order.java deleted file mode 100644 index 96a30fd0a..000000000 --- a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/Order.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.iluwatar.interceptingfilter; - -/** - * Order class carries the order data. - * - */ -public class Order { - - private String name; - private String contactNumber; - private String address; - private String depositNumber; - private String order; - - public Order() { - } - - public Order(String name, String contactNumber, String address, String depositNumber, String order) { - this.name = name; - this.contactNumber = contactNumber; - this.address = address; - this.depositNumber = depositNumber; - this.order = order; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getContactNumber() { - return contactNumber; - } - - public void setContactNumber(String contactNumber) { - this.contactNumber = contactNumber; - } - - public String getAddress() { - return address; - } - - public void setAddress(String address) { - this.address = address; - } - - public String getDepositNumber() { - return depositNumber; - } - - public void setDepositNumber(String depositNumber) { - this.depositNumber = depositNumber; - } - - public String getOrder() { - return order; - } - - public void setOrder(String order) { - this.order = order; - } -} diff --git a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/OrderFilter.java b/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/OrderFilter.java deleted file mode 100644 index b85fa3079..000000000 --- a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/OrderFilter.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.iluwatar.interceptingfilter; - -/** - * Concrete implementation of filter This checks for the order field - * - * @author joshzambales - * - */ -public class OrderFilter extends AbstractFilter { - - @Override - public String execute(Order order) { - String result = super.execute(order); - if (order.getOrder() == null || order.getOrder().isEmpty()) { - return result + "Invalid order! "; - } else { - return result; - } - } -} diff --git a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/Target.java b/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/Target.java deleted file mode 100644 index 2e68c2380..000000000 --- a/intercepting-filter/src/main/java/com/iluwatar/interceptingfilter/Target.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.iluwatar.interceptingfilter; - -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JPanel; -import javax.swing.JRootPane; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.SwingUtilities; -import javax.swing.table.DefaultTableModel; - -/** - * This is where the requests are displayed after being validated by filters. - * - * @author mjoshzambales - * - */ -public class Target extends JFrame { - - private static final long serialVersionUID = 1L; - - private JTable jt; - private JScrollPane jsp; - private DefaultTableModel dtm; - private JButton del; - - public Target() { - super("Order System"); - setDefaultCloseOperation(EXIT_ON_CLOSE); - setSize(640, 480); - dtm = new DefaultTableModel(new Object[] { "Name", "Contact Number", - "Address", "Deposit Number", "Order" }, 0); - jt = new JTable(dtm); - del = new JButton("Delete"); - setup(); - } - - private void setup() { - setLayout(new BorderLayout()); - JPanel bot = new JPanel(); - add(jt.getTableHeader(), BorderLayout.NORTH); - bot.setLayout(new BorderLayout()); - bot.add(del, BorderLayout.EAST); - add(bot, BorderLayout.SOUTH); - jsp = new JScrollPane(jt); - jsp.setPreferredSize(new Dimension(500, 250)); - add(jsp, BorderLayout.CENTER); - - del.addActionListener(new DListener()); - - JRootPane rootPane = SwingUtilities.getRootPane(del); - rootPane.setDefaultButton(del); - setVisible(true); - } - - public void execute(String[] request) { - dtm.addRow(new Object[] { request[0], request[1], request[2], - request[3], request[4] }); - } - - class DListener implements ActionListener { - @Override - public void actionPerformed(ActionEvent e) { - int temp = jt.getSelectedRow(); - if (temp == -1) - return; - int temp2 = jt.getSelectedRowCount(); - for (int i = 0; i < temp2; i++) { - dtm.removeRow(temp); - } - } - } -} diff --git a/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/AppTest.java b/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/AppTest.java new file mode 100644 index 000000000..c91fc3297 --- /dev/null +++ b/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/AppTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.intercepting.filter; + +import org.junit.Test; + +/** + * + * Application test. + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/FilterManagerTest.java b/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/FilterManagerTest.java new file mode 100644 index 000000000..4d7187727 --- /dev/null +++ b/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/FilterManagerTest.java @@ -0,0 +1,68 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.intercepting.filter; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +/** + * Date: 12/13/15 - 3:01 PM + * + * @author Jeroen Meulemeester + */ +public class FilterManagerTest { + + @Test + public void testFilterRequest() throws Exception { + final Target target = mock(Target.class); + final FilterManager filterManager = new FilterManager(); + assertEquals("RUNNING...", filterManager.filterRequest(mock(Order.class))); + verifyZeroInteractions(target); + } + + @Test + public void testAddFilter() throws Exception { + final Target target = mock(Target.class); + final FilterManager filterManager = new FilterManager(); + + verifyZeroInteractions(target); + + final Filter filter = mock(Filter.class); + when(filter.execute(any(Order.class))).thenReturn("filter"); + + filterManager.addFilter(filter); + + final Order order = mock(Order.class); + assertEquals("filter", filterManager.filterRequest(order)); + + verify(filter, times(1)).execute(any(Order.class)); + verifyZeroInteractions(target, filter, order); + } +} \ No newline at end of file diff --git a/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/FilterTest.java b/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/FilterTest.java new file mode 100644 index 000000000..c18a743d2 --- /dev/null +++ b/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/FilterTest.java @@ -0,0 +1,120 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.intercepting.filter; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.util.ArrayList; +import java.util.List; + +import static junit.framework.TestCase.assertSame; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +/** + * Date: 12/13/15 - 2:17 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class FilterTest { + + private static final Order PERFECT_ORDER = new Order("name", "12345678901", "addr", "dep", "order"); + private static final Order WRONG_ORDER = new Order("name", "12345678901", "addr", "dep", ""); + private static final Order WRONG_DEPOSIT = new Order("name", "12345678901", "addr", "", "order"); + private static final Order WRONG_ADDRESS = new Order("name", "12345678901", "", "dep", "order"); + private static final Order WRONG_CONTACT = new Order("name", "", "addr", "dep", "order"); + private static final Order WRONG_NAME = new Order("", "12345678901", "addr", "dep", "order"); + + @Parameters + public static List getTestData() { + final List testData = new ArrayList<>(); + testData.add(new Object[]{new NameFilter(), PERFECT_ORDER, ""}); + testData.add(new Object[]{new NameFilter(), WRONG_NAME, "Invalid name!"}); + testData.add(new Object[]{new NameFilter(), WRONG_CONTACT, ""}); + testData.add(new Object[]{new NameFilter(), WRONG_ADDRESS, ""}); + testData.add(new Object[]{new NameFilter(), WRONG_DEPOSIT, ""}); + testData.add(new Object[]{new NameFilter(), WRONG_ORDER, ""}); + + testData.add(new Object[]{new ContactFilter(), PERFECT_ORDER, ""}); + testData.add(new Object[]{new ContactFilter(), WRONG_NAME, ""}); + testData.add(new Object[]{new ContactFilter(), WRONG_CONTACT, "Invalid contact number!"}); + testData.add(new Object[]{new ContactFilter(), WRONG_ADDRESS, ""}); + testData.add(new Object[]{new ContactFilter(), WRONG_DEPOSIT, ""}); + testData.add(new Object[]{new ContactFilter(), WRONG_ORDER, ""}); + + testData.add(new Object[]{new AddressFilter(), PERFECT_ORDER, ""}); + testData.add(new Object[]{new AddressFilter(), WRONG_NAME, ""}); + testData.add(new Object[]{new AddressFilter(), WRONG_CONTACT, ""}); + testData.add(new Object[]{new AddressFilter(), WRONG_ADDRESS, "Invalid address!"}); + testData.add(new Object[]{new AddressFilter(), WRONG_DEPOSIT, ""}); + testData.add(new Object[]{new AddressFilter(), WRONG_ORDER, ""}); + + testData.add(new Object[]{new DepositFilter(), PERFECT_ORDER, ""}); + testData.add(new Object[]{new DepositFilter(), WRONG_NAME, ""}); + testData.add(new Object[]{new DepositFilter(), WRONG_CONTACT, ""}); + testData.add(new Object[]{new DepositFilter(), WRONG_ADDRESS, ""}); + testData.add(new Object[]{new DepositFilter(), WRONG_DEPOSIT, "Invalid deposit number!"}); + testData.add(new Object[]{new DepositFilter(), WRONG_ORDER, ""}); + + testData.add(new Object[]{new OrderFilter(), PERFECT_ORDER, ""}); + testData.add(new Object[]{new OrderFilter(), WRONG_NAME, ""}); + testData.add(new Object[]{new OrderFilter(), WRONG_CONTACT, ""}); + testData.add(new Object[]{new OrderFilter(), WRONG_ADDRESS, ""}); + testData.add(new Object[]{new OrderFilter(), WRONG_DEPOSIT, ""}); + testData.add(new Object[]{new OrderFilter(), WRONG_ORDER, "Invalid order!"}); + + return testData; + } + + private final Filter filter; + private final Order order; + private final String result; + + /** + * Constructor + */ + public FilterTest(Filter filter, Order order, String result) { + this.filter = filter; + this.order = order; + this.result = result; + } + + @Test + public void testExecute() throws Exception { + final String result = this.filter.execute(this.order); + assertNotNull(result); + assertEquals(this.result, result.trim()); + } + + @Test + public void testNext() throws Exception { + assertNull(this.filter.getNext()); + assertSame(this.filter, this.filter.getLast()); + } + +} diff --git a/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/OrderTest.java b/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/OrderTest.java new file mode 100644 index 000000000..5ef98b872 --- /dev/null +++ b/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/OrderTest.java @@ -0,0 +1,73 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.intercepting.filter; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Date: 12/13/15 - 2:57 PM + * + * @author Jeroen Meulemeester + */ +public class OrderTest { + + private static final String EXPECTED_VALUE = "test"; + + @Test + public void testSetName() throws Exception { + final Order order = new Order(); + order.setName(EXPECTED_VALUE); + assertEquals(EXPECTED_VALUE, order.getName()); + } + + @Test + public void testSetContactNumber() throws Exception { + final Order order = new Order(); + order.setContactNumber(EXPECTED_VALUE); + assertEquals(EXPECTED_VALUE, order.getContactNumber()); + } + + @Test + public void testSetAddress() throws Exception { + final Order order = new Order(); + order.setAddress(EXPECTED_VALUE); + assertEquals(EXPECTED_VALUE, order.getAddress()); + } + + @Test + public void testSetDepositNumber() throws Exception { + final Order order = new Order(); + order.setDepositNumber(EXPECTED_VALUE); + assertEquals(EXPECTED_VALUE, order.getDepositNumber()); + } + + @Test + public void testSetOrder() throws Exception { + final Order order = new Order(); + order.setOrder(EXPECTED_VALUE); + assertEquals(EXPECTED_VALUE, order.getOrder()); + } + +} diff --git a/interpreter/README.md b/interpreter/README.md new file mode 100644 index 000000000..87c1c47f7 --- /dev/null +++ b/interpreter/README.md @@ -0,0 +1,30 @@ +--- +layout: pattern +title: Interpreter +folder: interpreter +permalink: /patterns/interpreter/ +categories: Behavioral +tags: + - Java + - Gang Of Four + - Difficulty-Intermediate +--- + +## Intent +Given a language, define a representation for its grammar along +with an interpreter that uses the representation to interpret sentences in the +language. + +![alt text](./etc/interpreter_1.png "Interpreter") + +## Applicability +Use the Interpreter pattern when there is a language to +interpret, and you can represent statements in the language as abstract syntax +trees. The Interpreter pattern works best when + +* the grammar is simple. For complex grammars, the class hierarchy for the grammar becomes large and unmanageable. Tools such as parser generators are a better alternative in such cases. They can interpret expressions without building abstract syntax trees, which can save space and possibly time +* efficiency is not a critical concern. The most efficient interpreters are usually not implemented by interpreting parse trees directly but by first translating them into another form. For example, regular expressions are often transformed into state machines. But even then, the translator can be implemented by the Interpreter pattern, so the pattern is still applicable + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/interpreter/pom.xml b/interpreter/pom.xml index d58404463..9f5dd31f3 100644 --- a/interpreter/pom.xml +++ b/interpreter/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT interpreter diff --git a/interpreter/src/main/java/com/iluwatar/interpreter/App.java b/interpreter/src/main/java/com/iluwatar/interpreter/App.java index 56924f6b0..708f06e6f 100644 --- a/interpreter/src/main/java/com/iluwatar/interpreter/App.java +++ b/interpreter/src/main/java/com/iluwatar/interpreter/App.java @@ -1,68 +1,95 @@ -package com.iluwatar.interpreter; - -import java.util.Stack; - -/** - * - * Interpreter pattern breaks sentences into expressions (Expression) that can - * be evaluated and as a whole form the result. - * - */ -public class App { - - /** - * - * Expressions can be evaluated using prefix, infix or postfix notations - * This sample uses postfix, where operator comes after the operands - * - */ - public static void main(String[] args) { - String tokenString = "4 3 2 - 1 + *"; - Stack stack = new Stack<>(); - - String[] tokenList = tokenString.split(" "); - for (String s : tokenList) { - if (isOperator(s)) { - Expression rightExpression = stack.pop(); - Expression leftExpression = stack.pop(); - System.out - .println(String.format( - "popped from stack left: %d right: %d", - leftExpression.interpret(), - rightExpression.interpret())); - Expression operator = getOperatorInstance(s, leftExpression, - rightExpression); - System.out.println(String.format("operator: %s", operator)); - int result = operator.interpret(); - NumberExpression resultExpression = new NumberExpression(result); - stack.push(resultExpression); - System.out.println(String.format("push result to stack: %d", - resultExpression.interpret())); - } else { - Expression i = new NumberExpression(s); - stack.push(i); - System.out.println(String.format("push to stack: %d", - i.interpret())); - } - } - System.out - .println(String.format("result: %d", stack.pop().interpret())); - } - - public static boolean isOperator(String s) { - return s.equals("+") || s.equals("-") || s.equals("*"); - } - - public static Expression getOperatorInstance(String s, Expression left, - Expression right) { - switch (s) { - case "+": - return new PlusExpression(left, right); - case "-": - return new MinusExpression(left, right); - case "*": - return new MultiplyExpression(left, right); - } - return null; - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.interpreter; + +import java.util.Stack; + +/** + * + * The Interpreter pattern is a design pattern that specifies how to evaluate sentences in a + * language. The basic idea is to have a class for each symbol (terminal or nonterminal) in a + * specialized computer language. The syntax tree of a sentence in the language is an instance of + * the composite pattern and is used to evaluate (interpret) the sentence for a client. + *

+ * In this example we use the Interpreter pattern to break sentences into expressions ( + * {@link Expression}) that can be evaluated and as a whole form the result. + * + */ +public class App { + + /** + * + * Program entry point. + *

+ * Expressions can be evaluated using prefix, infix or postfix notations This sample uses postfix, + * where operator comes after the operands + * + * @param args command line args + * + */ + public static void main(String[] args) { + String tokenString = "4 3 2 - 1 + *"; + Stack stack = new Stack<>(); + + String[] tokenList = tokenString.split(" "); + for (String s : tokenList) { + if (isOperator(s)) { + Expression rightExpression = stack.pop(); + Expression leftExpression = stack.pop(); + System.out.println(String.format("popped from stack left: %d right: %d", + leftExpression.interpret(), rightExpression.interpret())); + Expression operator = getOperatorInstance(s, leftExpression, rightExpression); + System.out.println(String.format("operator: %s", operator)); + int result = operator.interpret(); + NumberExpression resultExpression = new NumberExpression(result); + stack.push(resultExpression); + System.out.println(String.format("push result to stack: %d", resultExpression.interpret())); + } else { + Expression i = new NumberExpression(s); + stack.push(i); + System.out.println(String.format("push to stack: %d", i.interpret())); + } + } + System.out.println(String.format("result: %d", stack.pop().interpret())); + } + + public static boolean isOperator(String s) { + return s.equals("+") || s.equals("-") || s.equals("*"); + } + + /** + * Get expression for string + */ + public static Expression getOperatorInstance(String s, Expression left, Expression right) { + switch (s) { + case "+": + return new PlusExpression(left, right); + case "-": + return new MinusExpression(left, right); + case "*": + return new MultiplyExpression(left, right); + default: + return new MultiplyExpression(left, right); + } + } +} diff --git a/interpreter/src/main/java/com/iluwatar/interpreter/Expression.java b/interpreter/src/main/java/com/iluwatar/interpreter/Expression.java index 877e5db33..20cdb512a 100644 --- a/interpreter/src/main/java/com/iluwatar/interpreter/Expression.java +++ b/interpreter/src/main/java/com/iluwatar/interpreter/Expression.java @@ -1,9 +1,36 @@ -package com.iluwatar.interpreter; - -public abstract class Expression { - - public abstract int interpret(); - - @Override - public abstract String toString(); -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.interpreter; + +/** + * + * Expression + * + */ +public abstract class Expression { + + public abstract int interpret(); + + @Override + public abstract String toString(); +} diff --git a/interpreter/src/main/java/com/iluwatar/interpreter/MinusExpression.java b/interpreter/src/main/java/com/iluwatar/interpreter/MinusExpression.java index 03d088604..53748baf8 100644 --- a/interpreter/src/main/java/com/iluwatar/interpreter/MinusExpression.java +++ b/interpreter/src/main/java/com/iluwatar/interpreter/MinusExpression.java @@ -1,23 +1,50 @@ -package com.iluwatar.interpreter; - -public class MinusExpression extends Expression { - - private Expression leftExpression; - private Expression rightExpression; - - public MinusExpression(Expression leftExpression, Expression rightExpression) { - this.leftExpression = leftExpression; - this.rightExpression = rightExpression; - } - - @Override - public int interpret() { - return leftExpression.interpret() - rightExpression.interpret(); - } - - @Override - public String toString() { - return "-"; - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.interpreter; + +/** + * + * MinusExpression + * + */ +public class MinusExpression extends Expression { + + private Expression leftExpression; + private Expression rightExpression; + + public MinusExpression(Expression leftExpression, Expression rightExpression) { + this.leftExpression = leftExpression; + this.rightExpression = rightExpression; + } + + @Override + public int interpret() { + return leftExpression.interpret() - rightExpression.interpret(); + } + + @Override + public String toString() { + return "-"; + } + +} diff --git a/interpreter/src/main/java/com/iluwatar/interpreter/MultiplyExpression.java b/interpreter/src/main/java/com/iluwatar/interpreter/MultiplyExpression.java index fa30e4236..bd060d3c0 100644 --- a/interpreter/src/main/java/com/iluwatar/interpreter/MultiplyExpression.java +++ b/interpreter/src/main/java/com/iluwatar/interpreter/MultiplyExpression.java @@ -1,24 +1,50 @@ -package com.iluwatar.interpreter; - -public class MultiplyExpression extends Expression { - - private Expression leftExpression; - private Expression rightExpression; - - public MultiplyExpression(Expression leftExpression, - Expression rightExpression) { - this.leftExpression = leftExpression; - this.rightExpression = rightExpression; - } - - @Override - public int interpret() { - return leftExpression.interpret() * rightExpression.interpret(); - } - - @Override - public String toString() { - return "*"; - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.interpreter; + +/** + * + * MultiplyExpression + * + */ +public class MultiplyExpression extends Expression { + + private Expression leftExpression; + private Expression rightExpression; + + public MultiplyExpression(Expression leftExpression, Expression rightExpression) { + this.leftExpression = leftExpression; + this.rightExpression = rightExpression; + } + + @Override + public int interpret() { + return leftExpression.interpret() * rightExpression.interpret(); + } + + @Override + public String toString() { + return "*"; + } + +} diff --git a/interpreter/src/main/java/com/iluwatar/interpreter/NumberExpression.java b/interpreter/src/main/java/com/iluwatar/interpreter/NumberExpression.java index 041ddccae..891c926bd 100644 --- a/interpreter/src/main/java/com/iluwatar/interpreter/NumberExpression.java +++ b/interpreter/src/main/java/com/iluwatar/interpreter/NumberExpression.java @@ -1,25 +1,51 @@ -package com.iluwatar.interpreter; - -public class NumberExpression extends Expression { - - private int number; - - public NumberExpression(int number) { - this.number = number; - } - - public NumberExpression(String s) { - this.number = Integer.parseInt(s); - } - - @Override - public int interpret() { - return number; - } - - @Override - public String toString() { - return "number"; - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.interpreter; + +/** + * + * NumberExpression + * + */ +public class NumberExpression extends Expression { + + private int number; + + public NumberExpression(int number) { + this.number = number; + } + + public NumberExpression(String s) { + this.number = Integer.parseInt(s); + } + + @Override + public int interpret() { + return number; + } + + @Override + public String toString() { + return "number"; + } +} diff --git a/interpreter/src/main/java/com/iluwatar/interpreter/PlusExpression.java b/interpreter/src/main/java/com/iluwatar/interpreter/PlusExpression.java index 843a66b62..066d536b1 100644 --- a/interpreter/src/main/java/com/iluwatar/interpreter/PlusExpression.java +++ b/interpreter/src/main/java/com/iluwatar/interpreter/PlusExpression.java @@ -1,23 +1,49 @@ -package com.iluwatar.interpreter; - -public class PlusExpression extends Expression { - - private Expression leftExpression; - private Expression rightExpression; - - public PlusExpression(Expression leftExpression, Expression rightExpression) { - this.leftExpression = leftExpression; - this.rightExpression = rightExpression; - } - - @Override - public int interpret() { - return leftExpression.interpret() + rightExpression.interpret(); - } - - @Override - public String toString() { - return "+"; - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.interpreter; + +/** + * + * PlusExpression + * + */ +public class PlusExpression extends Expression { + + private Expression leftExpression; + private Expression rightExpression; + + public PlusExpression(Expression leftExpression, Expression rightExpression) { + this.leftExpression = leftExpression; + this.rightExpression = rightExpression; + } + + @Override + public int interpret() { + return leftExpression.interpret() + rightExpression.interpret(); + } + + @Override + public String toString() { + return "+"; + } +} diff --git a/interpreter/src/test/java/com/iluwatar/interpreter/AppTest.java b/interpreter/src/test/java/com/iluwatar/interpreter/AppTest.java index f89150159..01b927e67 100644 --- a/interpreter/src/test/java/com/iluwatar/interpreter/AppTest.java +++ b/interpreter/src/test/java/com/iluwatar/interpreter/AppTest.java @@ -1,14 +1,39 @@ -package com.iluwatar.interpreter; - -import org.junit.Test; - -import com.iluwatar.interpreter.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.interpreter; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/interpreter/src/test/java/com/iluwatar/interpreter/ExpressionTest.java b/interpreter/src/test/java/com/iluwatar/interpreter/ExpressionTest.java new file mode 100644 index 000000000..d07ee84fb --- /dev/null +++ b/interpreter/src/test/java/com/iluwatar/interpreter/ExpressionTest.java @@ -0,0 +1,133 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.interpreter; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiFunction; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/14/15 - 11:48 AM + * + * @author Jeroen Meulemeester + */ +public abstract class ExpressionTest { + + /** + * Generate inputs ranging from -10 to 10 for both input params and calculate the expected result + * + * @param resultCalc The function used to calculate the expected result + * @return A data set with test entries + */ + static List prepareParameters(final BiFunction resultCalc) { + final List testData = new ArrayList<>(); + for (int i = -10; i < 10; i++) { + for (int j = -10; j < 10; j++) { + testData.add(new Object[]{ + new NumberExpression(i), + new NumberExpression(j), + resultCalc.apply(i, j) + }); + } + } + return testData; + } + + /** + * The input used as first parameter during the test + */ + private final NumberExpression first; + + /** + * The input used as second parameter during the test + */ + private final NumberExpression second; + + /** + * The expected result of the calculation, taking the first and second parameter in account + */ + private final int result; + + /** + * The expected {@link E#toString()} response + */ + private final String expectedToString; + + /** + * Factory, used to create a new test object instance with the correct first and second parameter + */ + private final BiFunction factory; + + /** + * Create a new test instance with the given parameters and expected results + * + * @param first The input used as first parameter during the test + * @param second The input used as second parameter during the test + * @param result The expected result of the tested expression + * @param expectedToString The expected {@link E#toString()} response + * @param factory Factory, used to create a new test object instance + */ + ExpressionTest(final NumberExpression first, final NumberExpression second, final int result, + final String expectedToString, final BiFunction factory) { + + this.first = first; + this.second = second; + this.result = result; + this.expectedToString = expectedToString; + this.factory = factory; + } + + /** + * Get the first parameter + * + * @return The first parameter + */ + final NumberExpression getFirst() { + return this.first; + } + + /** + * Verify if the expression calculates the correct result when calling {@link E#interpret()} + */ + @Test + public void testInterpret() { + final E expression = this.factory.apply(this.first, this.second); + assertNotNull(expression); + assertEquals(this.result, expression.interpret()); + } + + /** + * Verify if the expression has the expected {@link E#toString()} value + */ + @Test + public void testToString() { + final E expression = this.factory.apply(this.first, this.second); + assertNotNull(expression); + assertEquals(expectedToString, expression.toString()); + } +} diff --git a/interpreter/src/test/java/com/iluwatar/interpreter/MinusExpressionTest.java b/interpreter/src/test/java/com/iluwatar/interpreter/MinusExpressionTest.java new file mode 100644 index 000000000..fe4ebc58f --- /dev/null +++ b/interpreter/src/test/java/com/iluwatar/interpreter/MinusExpressionTest.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.interpreter; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.util.List; + +/** + * Date: 12/14/15 - 12:08 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class MinusExpressionTest extends ExpressionTest { + + /** + * Create a new set of test entries with the expected result + * + * @return The list of parameters used during this test + */ + @Parameters + public static List data() { + return prepareParameters((f, s) -> f - s); + } + + /** + * Create a new test instance using the given test parameters and expected result + * + * @param first The first expression parameter + * @param second The second expression parameter + * @param result The expected result + */ + public MinusExpressionTest(final NumberExpression first, final NumberExpression second, final int result) { + super(first, second, result, "-", MinusExpression::new); + } + +} \ No newline at end of file diff --git a/interpreter/src/test/java/com/iluwatar/interpreter/MultiplyExpressionTest.java b/interpreter/src/test/java/com/iluwatar/interpreter/MultiplyExpressionTest.java new file mode 100644 index 000000000..f876dda34 --- /dev/null +++ b/interpreter/src/test/java/com/iluwatar/interpreter/MultiplyExpressionTest.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.interpreter; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.util.List; + +/** + * Date: 12/14/15 - 12:08 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class MultiplyExpressionTest extends ExpressionTest { + + /** + * Create a new set of test entries with the expected result + * + * @return The list of parameters used during this test + */ + @Parameters + public static List data() { + return prepareParameters((f, s) -> f * s); + } + + /** + * Create a new test instance using the given test parameters and expected result + * + * @param first The first expression parameter + * @param second The second expression parameter + * @param result The expected result + */ + public MultiplyExpressionTest(final NumberExpression first, final NumberExpression second, final int result) { + super(first, second, result, "*", MultiplyExpression::new); + } + +} \ No newline at end of file diff --git a/interpreter/src/test/java/com/iluwatar/interpreter/NumberExpressionTest.java b/interpreter/src/test/java/com/iluwatar/interpreter/NumberExpressionTest.java new file mode 100644 index 000000000..d055fcd47 --- /dev/null +++ b/interpreter/src/test/java/com/iluwatar/interpreter/NumberExpressionTest.java @@ -0,0 +1,74 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.interpreter; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.util.List; + +import static org.junit.Assert.assertEquals; + +/** + * Date: 12/14/15 - 12:08 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class NumberExpressionTest extends ExpressionTest { + + /** + * Create a new set of test entries with the expected result + * + * @return The list of parameters used during this test + */ + @Parameters + public static List data() { + return prepareParameters((f, s) -> f); + } + + /** + * Create a new test instance using the given test parameters and expected result + * + * @param first The first expression parameter + * @param second The second expression parameter + * @param result The expected result + */ + public NumberExpressionTest(final NumberExpression first, final NumberExpression second, final int result) { + super(first, second, result, "number", (f, s) -> f); + } + + /** + * Verify if the {@link NumberExpression#NumberExpression(String)} constructor works as expected + */ + @Test + public void testFromString() throws Exception { + final int expectedValue = getFirst().interpret(); + final String testStingValue = String.valueOf(expectedValue); + final NumberExpression numberExpression = new NumberExpression(testStingValue); + assertEquals(expectedValue, numberExpression.interpret()); + } + +} \ No newline at end of file diff --git a/interpreter/src/test/java/com/iluwatar/interpreter/PlusExpressionTest.java b/interpreter/src/test/java/com/iluwatar/interpreter/PlusExpressionTest.java new file mode 100644 index 000000000..e99d49555 --- /dev/null +++ b/interpreter/src/test/java/com/iluwatar/interpreter/PlusExpressionTest.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.interpreter; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.util.List; + +/** + * Date: 12/14/15 - 12:08 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class PlusExpressionTest extends ExpressionTest { + + /** + * Create a new set of test entries with the expected result + * + * @return The list of parameters used during this test + */ + @Parameters + public static List data() { + return prepareParameters((f, s) -> f + s); + } + + /** + * Create a new test instance using the given test parameters and expected result + * + * @param first The first expression parameter + * @param second The second expression parameter + * @param result The expected result + */ + public PlusExpressionTest(final NumberExpression first, final NumberExpression second, final int result) { + super(first, second, result, "+", PlusExpression::new); + } + +} \ No newline at end of file diff --git a/iterator/README.md b/iterator/README.md new file mode 100644 index 000000000..d6be7758d --- /dev/null +++ b/iterator/README.md @@ -0,0 +1,35 @@ +--- +layout: pattern +title: Iterator +folder: iterator +permalink: /patterns/iterator/ +categories: Behavioral +tags: + - Java + - Difficulty-Beginner + - Gang Of Four +--- + +## Also known as +Cursor + +## Intent +Provide a way to access the elements of an aggregate object +sequentially without exposing its underlying representation. + +![alt text](./etc/iterator_1.png "Iterator") + +## Applicability +Use the Iterator pattern + +* to access an aggregate object's contents without exposing its internal representation +* to support multiple traversals of aggregate objects +* to provide a uniform interface for traversing different aggregate structures + +## Real world examples + +* [java.util.Iterator](http://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html) + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/iterator/pom.xml b/iterator/pom.xml index 48b91d9e5..484030a33 100644 --- a/iterator/pom.xml +++ b/iterator/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT iterator diff --git a/iterator/src/main/java/com/iluwatar/iterator/App.java b/iterator/src/main/java/com/iluwatar/iterator/App.java index 22a6ea2d7..8da0a7433 100644 --- a/iterator/src/main/java/com/iluwatar/iterator/App.java +++ b/iterator/src/main/java/com/iluwatar/iterator/App.java @@ -1,41 +1,71 @@ -package com.iluwatar.iterator; - -/** - * - * Iterator (ItemIterator) adds abstraction layer on top of a collection - * (TreasureChest). This way the collection can change its internal - * implementation without affecting its clients. - * - */ -public class App { - - public static void main(String[] args) { - TreasureChest chest = new TreasureChest(); - - ItemIterator ringIterator = chest.Iterator(ItemType.RING); - while (ringIterator.hasNext()) { - System.out.println(ringIterator.next()); - } - - System.out.println("----------"); - - ItemIterator potionIterator = chest.Iterator(ItemType.POTION); - while (potionIterator.hasNext()) { - System.out.println(potionIterator.next()); - } - - System.out.println("----------"); - - ItemIterator weaponIterator = chest.Iterator(ItemType.WEAPON); - while (weaponIterator.hasNext()) { - System.out.println(weaponIterator.next()); - } - - System.out.println("----------"); - - ItemIterator it = chest.Iterator(ItemType.ANY); - while (it.hasNext()) { - System.out.println(it.next()); - } - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.iterator; + +/** + * + * The Iterator pattern is a design pattern in which an iterator is used to traverse a container and + * access the container's elements. The Iterator pattern decouples algorithms from containers. + *

+ * In this example the Iterator ({@link ItemIterator}) adds abstraction layer on top of a collection + * ({@link TreasureChest}). This way the collection can change its internal implementation without + * affecting its clients. + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + TreasureChest chest = new TreasureChest(); + + ItemIterator ringIterator = chest.iterator(ItemType.RING); + while (ringIterator.hasNext()) { + System.out.println(ringIterator.next()); + } + + System.out.println("----------"); + + ItemIterator potionIterator = chest.iterator(ItemType.POTION); + while (potionIterator.hasNext()) { + System.out.println(potionIterator.next()); + } + + System.out.println("----------"); + + ItemIterator weaponIterator = chest.iterator(ItemType.WEAPON); + while (weaponIterator.hasNext()) { + System.out.println(weaponIterator.next()); + } + + System.out.println("----------"); + + ItemIterator it = chest.iterator(ItemType.ANY); + while (it.hasNext()) { + System.out.println(it.next()); + } + } +} diff --git a/iterator/src/main/java/com/iluwatar/iterator/Item.java b/iterator/src/main/java/com/iluwatar/iterator/Item.java index c22eba96f..8bb540fbd 100644 --- a/iterator/src/main/java/com/iluwatar/iterator/Item.java +++ b/iterator/src/main/java/com/iluwatar/iterator/Item.java @@ -1,25 +1,52 @@ -package com.iluwatar.iterator; - -public class Item { - - private ItemType type; - private String name; - - public Item(ItemType type, String name) { - this.setType(type); - this.name = name; - } - - @Override - public String toString() { - return name; - } - - public ItemType getType() { - return type; - } - - public void setType(ItemType type) { - this.type = type; - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.iterator; + +/** + * + * Item + * + */ +public class Item { + + private ItemType type; + private String name; + + public Item(ItemType type, String name) { + this.setType(type); + this.name = name; + } + + @Override + public String toString() { + return name; + } + + public ItemType getType() { + return type; + } + + public final void setType(ItemType type) { + this.type = type; + } +} diff --git a/iterator/src/main/java/com/iluwatar/iterator/ItemIterator.java b/iterator/src/main/java/com/iluwatar/iterator/ItemIterator.java index b18cd608e..fa1ff633b 100644 --- a/iterator/src/main/java/com/iluwatar/iterator/ItemIterator.java +++ b/iterator/src/main/java/com/iluwatar/iterator/ItemIterator.java @@ -1,13 +1,35 @@ -package com.iluwatar.iterator; - -/** - * - * Iterator interface. - * - */ -public interface ItemIterator { - - boolean hasNext(); - - Item next(); -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.iterator; + +/** + * + * ItemIterator interface. + * + */ +public interface ItemIterator { + + boolean hasNext(); + + Item next(); +} diff --git a/iterator/src/main/java/com/iluwatar/iterator/ItemType.java b/iterator/src/main/java/com/iluwatar/iterator/ItemType.java index a3d4d3f54..0b1def25b 100644 --- a/iterator/src/main/java/com/iluwatar/iterator/ItemType.java +++ b/iterator/src/main/java/com/iluwatar/iterator/ItemType.java @@ -1,7 +1,34 @@ -package com.iluwatar.iterator; - -public enum ItemType { - - ANY, WEAPON, RING, POTION - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.iterator; + +/** + * + * ItemType enumeration + * + */ +public enum ItemType { + + ANY, WEAPON, RING, POTION + +} diff --git a/iterator/src/main/java/com/iluwatar/iterator/TreasureChest.java b/iterator/src/main/java/com/iluwatar/iterator/TreasureChest.java index e20269023..2c5d698e9 100644 --- a/iterator/src/main/java/com/iluwatar/iterator/TreasureChest.java +++ b/iterator/src/main/java/com/iluwatar/iterator/TreasureChest.java @@ -1,39 +1,67 @@ -package com.iluwatar.iterator; - -import java.util.ArrayList; -import java.util.List; - -/** - * - * Collection class. - * - */ -public class TreasureChest { - - private List items; - - public TreasureChest() { - items = new ArrayList<>(); - items.add(new Item(ItemType.POTION, "Potion of courage")); - items.add(new Item(ItemType.RING, "Ring of shadows")); - items.add(new Item(ItemType.POTION, "Potion of wisdom")); - items.add(new Item(ItemType.POTION, "Potion of blood")); - items.add(new Item(ItemType.WEAPON, "Sword of silver +1")); - items.add(new Item(ItemType.POTION, "Potion of rust")); - items.add(new Item(ItemType.POTION, "Potion of healing")); - items.add(new Item(ItemType.RING, "Ring of armor")); - items.add(new Item(ItemType.WEAPON, "Steel halberd")); - items.add(new Item(ItemType.WEAPON, "Dagger of poison")); - } - - ItemIterator Iterator(ItemType type) { - return new TreasureChestItemIterator(this, type); - } - - public List getItems() { - ArrayList list = new ArrayList<>(); - list.addAll(items); - return list; - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.iterator; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * TreasureChest, the collection class. + * + */ +public class TreasureChest { + + private List items; + + /** + * Constructor + */ + public TreasureChest() { + items = new ArrayList<>(); + items.add(new Item(ItemType.POTION, "Potion of courage")); + items.add(new Item(ItemType.RING, "Ring of shadows")); + items.add(new Item(ItemType.POTION, "Potion of wisdom")); + items.add(new Item(ItemType.POTION, "Potion of blood")); + items.add(new Item(ItemType.WEAPON, "Sword of silver +1")); + items.add(new Item(ItemType.POTION, "Potion of rust")); + items.add(new Item(ItemType.POTION, "Potion of healing")); + items.add(new Item(ItemType.RING, "Ring of armor")); + items.add(new Item(ItemType.WEAPON, "Steel halberd")); + items.add(new Item(ItemType.WEAPON, "Dagger of poison")); + } + + ItemIterator iterator(ItemType itemType) { + return new TreasureChestItemIterator(this, itemType); + } + + /** + * Get all items + */ + public List getItems() { + List list = new ArrayList<>(); + list.addAll(items); + return list; + } + +} diff --git a/iterator/src/main/java/com/iluwatar/iterator/TreasureChestItemIterator.java b/iterator/src/main/java/com/iluwatar/iterator/TreasureChestItemIterator.java index fca50725d..7c80422c2 100644 --- a/iterator/src/main/java/com/iluwatar/iterator/TreasureChestItemIterator.java +++ b/iterator/src/main/java/com/iluwatar/iterator/TreasureChestItemIterator.java @@ -1,49 +1,78 @@ -package com.iluwatar.iterator; - -import java.util.List; - -public class TreasureChestItemIterator implements ItemIterator { - - private TreasureChest chest; - private int idx; - private ItemType type; - - public TreasureChestItemIterator(TreasureChest chest, ItemType type) { - this.chest = chest; - this.type = type; - this.idx = -1; - } - - @Override - public boolean hasNext() { - return findNextIdx() != -1; - } - - @Override - public Item next() { - idx = findNextIdx(); - if (idx != -1) { - return chest.getItems().get(idx); - } - return null; - } - - private int findNextIdx() { - - List items = chest.getItems(); - boolean found = false; - int tempIdx = idx; - while (!found) { - tempIdx++; - if (tempIdx >= items.size()) { - tempIdx = -1; - break; - } - if (type.equals(ItemType.ANY) - || items.get(tempIdx).getType().equals(type)) { - break; - } - } - return tempIdx; - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.iterator; + +import java.util.List; + +/** + * + * TreasureChestItemIterator + * + */ +public class TreasureChestItemIterator implements ItemIterator { + + private TreasureChest chest; + private int idx; + private ItemType type; + + /** + * Constructor + */ + public TreasureChestItemIterator(TreasureChest chest, ItemType type) { + this.chest = chest; + this.type = type; + this.idx = -1; + } + + @Override + public boolean hasNext() { + return findNextIdx() != -1; + } + + @Override + public Item next() { + idx = findNextIdx(); + if (idx != -1) { + return chest.getItems().get(idx); + } + return null; + } + + private int findNextIdx() { + + List items = chest.getItems(); + boolean found = false; + int tempIdx = idx; + while (!found) { + tempIdx++; + if (tempIdx >= items.size()) { + tempIdx = -1; + break; + } + if (type.equals(ItemType.ANY) || items.get(tempIdx).getType().equals(type)) { + break; + } + } + return tempIdx; + } +} diff --git a/iterator/src/test/java/com/iluwatar/iterator/AppTest.java b/iterator/src/test/java/com/iluwatar/iterator/AppTest.java index ffdbb9ba1..7259386bb 100644 --- a/iterator/src/test/java/com/iluwatar/iterator/AppTest.java +++ b/iterator/src/test/java/com/iluwatar/iterator/AppTest.java @@ -1,14 +1,39 @@ -package com.iluwatar.iterator; - -import org.junit.Test; - -import com.iluwatar.iterator.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.iterator; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/iterator/src/test/java/com/iluwatar/iterator/TreasureChestTest.java b/iterator/src/test/java/com/iluwatar/iterator/TreasureChestTest.java new file mode 100644 index 000000000..9bef322d6 --- /dev/null +++ b/iterator/src/test/java/com/iluwatar/iterator/TreasureChestTest.java @@ -0,0 +1,130 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.iterator; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +/** + * Date: 12/14/15 - 2:58 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class TreasureChestTest { + + /** + * Create a list of all expected items in the chest. + * + * @return The set of all expected items in the chest + */ + @Parameterized.Parameters + public static List data() { + final List parameters = new ArrayList<>(); + parameters.add(new Object[]{new Item(ItemType.POTION, "Potion of courage")}); + parameters.add(new Object[]{new Item(ItemType.RING, "Ring of shadows")}); + parameters.add(new Object[]{new Item(ItemType.POTION, "Potion of wisdom")}); + parameters.add(new Object[]{new Item(ItemType.POTION, "Potion of blood")}); + parameters.add(new Object[]{new Item(ItemType.WEAPON, "Sword of silver +1")}); + parameters.add(new Object[]{new Item(ItemType.POTION, "Potion of rust")}); + parameters.add(new Object[]{new Item(ItemType.POTION, "Potion of healing")}); + parameters.add(new Object[]{new Item(ItemType.RING, "Ring of armor")}); + parameters.add(new Object[]{new Item(ItemType.WEAPON, "Steel halberd")}); + parameters.add(new Object[]{new Item(ItemType.WEAPON, "Dagger of poison")}); + return parameters; + } + + /** + * One of the expected items in the chest + */ + private final Item expectedItem; + + /** + * Create a new test instance, test if the given expected item can be retrieved from the chest + * + * @param expectedItem One of the items that should be in the chest + */ + public TreasureChestTest(final Item expectedItem) { + this.expectedItem = expectedItem; + } + + /** + * Test if the expected item can be retrieved from the chest using the {@link ItemIterator} + */ + @Test + public void testIterator() { + final TreasureChest chest = new TreasureChest(); + final ItemIterator iterator = chest.iterator(expectedItem.getType()); + assertNotNull(iterator); + + while (iterator.hasNext()) { + final Item item = iterator.next(); + assertNotNull(item); + assertEquals(this.expectedItem.getType(), item.getType()); + + final String name = item.toString(); + assertNotNull(name); + if (this.expectedItem.toString().equals(name)) { + return; + } + } + + fail("Expected to find item [" + this.expectedItem + "] using iterator, but we didn't."); + + } + + /** + * Test if the expected item can be retrieved from the chest using the {@link + * TreasureChest#getItems()} method + */ + @Test + public void testGetItems() throws Exception { + final TreasureChest chest = new TreasureChest(); + final List items = chest.getItems(); + assertNotNull(items); + + for (final Item item : items) { + assertNotNull(item); + assertNotNull(item.getType()); + assertNotNull(item.toString()); + + final boolean sameType = this.expectedItem.getType() == item.getType(); + final boolean sameName = this.expectedItem.toString().equals(item.toString()); + if (sameType && sameName) { + return; + } + } + + fail("Expected to find item [" + this.expectedItem + "] in the item list, but we didn't."); + + } + +} \ No newline at end of file diff --git a/layers/README.md b/layers/README.md new file mode 100644 index 000000000..8e8eda366 --- /dev/null +++ b/layers/README.md @@ -0,0 +1,28 @@ +--- +layout: pattern +title: Layers +folder: layers +permalink: /patterns/layers/ +categories: Architectural +tags: + - Java + - Difficulty-Intermediate + - Spring +--- + +## Intent +Layers is an architectural style where software responsibilities are + divided among the different layers of the application. + +![alt text](./etc/layers.png "Layers") + +## Applicability +Use the Layers architecture when + +* you want clearly divide software responsibilities into differents parts of the program +* you want to prevent a change from propagating throughout the application +* you want to make your application more maintainable and testable + +## Credits + +* [Pattern Oriented Software Architecture Vol I-V](http://www.amazon.com/Pattern-Oriented-Software-Architecture-Volume-Patterns/dp/0471958697) diff --git a/layers/etc/layers.png b/layers/etc/layers.png new file mode 100644 index 000000000..a4bd8b19d Binary files /dev/null and b/layers/etc/layers.png differ diff --git a/layers/etc/layers.ucls b/layers/etc/layers.ucls new file mode 100644 index 000000000..060b391c0 --- /dev/null +++ b/layers/etc/layers.ucls @@ -0,0 +1,256 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/layers/pom.xml b/layers/pom.xml new file mode 100644 index 000000000..bf155b655 --- /dev/null +++ b/layers/pom.xml @@ -0,0 +1,65 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + com.iluwatar.layers + layers + + + org.springframework.data + spring-data-jpa + + + org.hibernate + hibernate-entitymanager + + + commons-dbcp + commons-dbcp + + + com.h2database + h2 + + + junit + junit + test + + + org.mockito + mockito-core + test + + + diff --git a/layers/src/main/java/com/iluwatar/layers/App.java b/layers/src/main/java/com/iluwatar/layers/App.java new file mode 100644 index 000000000..f8a1648bf --- /dev/null +++ b/layers/src/main/java/com/iluwatar/layers/App.java @@ -0,0 +1,118 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.layers; + +import java.util.Arrays; + +/** + * + * Layers is an architectural style where software responsibilities are divided among the different layers of the + * application. + *

+ * This example demonstrates a traditional 3-layer architecture consisting of data access layer, business layer and + * presentation layer. + *

+ * The data access layer is formed of Spring Data repositories CakeDao, CakeToppingDao and + * CakeLayerDao. The repositories can be used for CRUD operations on cakes, cake toppings and cake layers + * respectively. + *

+ * The business layer is built on top of the data access layer. CakeBakingService offers methods to + * retrieve available cake toppings and cake layers and baked cakes. Also the service is used to create new cakes out of + * cake toppings and cake layers. + *

+ * The presentation layer is built on the business layer and in this example it simply lists the cakes that have been + * baked. + *

+ * We have applied so called strict layering which means that the layers can only access the classes directly beneath + * them. This leads the solution to create an additional set of DTOs ( CakeInfo, + * CakeToppingInfo, CakeLayerInfo) to translate data between layers. In other words, + * CakeBakingService cannot return entities ( Cake, CakeTopping, + * CakeLayer) directly since these reside on data access layer but instead translates these into business + * layer DTOs (CakeInfo, CakeToppingInfo, CakeLayerInfo) and returns them + * instead. This way the presentation layer does not have any knowledge of other layers than the business layer and thus + * is not affected by changes to them. + * + * @see Cake + * @see CakeTopping + * @see CakeLayer + * @see CakeDao + * @see CakeToppingDao + * @see CakeLayerDao + * @see CakeBakingService + * @see CakeInfo + * @see CakeToppingInfo + * @see CakeLayerInfo + * + */ +public class App { + + private static CakeBakingService cakeBakingService = new CakeBakingServiceImpl(); + + /** + * Application entry point + * + * @param args Command line parameters + */ + public static void main(String[] args) { + + // initialize example data + initializeData(cakeBakingService); + + // create view and render it + CakeViewImpl cakeView = new CakeViewImpl(cakeBakingService); + cakeView.render(); + } + + /** + * Initializes the example data + */ + private static void initializeData(CakeBakingService cakeBakingService) { + cakeBakingService.saveNewLayer(new CakeLayerInfo("chocolate", 1200)); + cakeBakingService.saveNewLayer(new CakeLayerInfo("banana", 900)); + cakeBakingService.saveNewLayer(new CakeLayerInfo("strawberry", 950)); + cakeBakingService.saveNewLayer(new CakeLayerInfo("lemon", 950)); + cakeBakingService.saveNewLayer(new CakeLayerInfo("vanilla", 950)); + cakeBakingService.saveNewLayer(new CakeLayerInfo("strawberry", 950)); + + cakeBakingService.saveNewTopping(new CakeToppingInfo("candies", 350)); + cakeBakingService.saveNewTopping(new CakeToppingInfo("cherry", 350)); + + CakeInfo cake1 = + new CakeInfo(new CakeToppingInfo("candies", 0), Arrays.asList(new CakeLayerInfo( + "chocolate", 0), new CakeLayerInfo("banana", 0), new CakeLayerInfo("strawberry", 0))); + try { + cakeBakingService.bakeNewCake(cake1); + } catch (CakeBakingException e) { + e.printStackTrace(); + } + CakeInfo cake2 = + new CakeInfo(new CakeToppingInfo("cherry", 0), Arrays.asList( + new CakeLayerInfo("vanilla", 0), new CakeLayerInfo("lemon", 0), new CakeLayerInfo( + "strawberry", 0))); + try { + cakeBakingService.bakeNewCake(cake2); + } catch (CakeBakingException e) { + e.printStackTrace(); + } + } +} diff --git a/layers/src/main/java/com/iluwatar/layers/Cake.java b/layers/src/main/java/com/iluwatar/layers/Cake.java new file mode 100644 index 000000000..f6eae2ad7 --- /dev/null +++ b/layers/src/main/java/com/iluwatar/layers/Cake.java @@ -0,0 +1,90 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.layers; + +import java.util.HashSet; +import java.util.Set; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.OneToOne; + +/** + * + * Cake entity + * + */ +@Entity +public class Cake { + + @Id + @GeneratedValue + private Long id; + + @OneToOne(cascade = CascadeType.REMOVE) + private CakeTopping topping; + + @OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER) + private Set layers; + + public Cake() { + setLayers(new HashSet<>()); + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public CakeTopping getTopping() { + return topping; + } + + public void setTopping(CakeTopping topping) { + this.topping = topping; + } + + public Set getLayers() { + return layers; + } + + public final void setLayers(Set layers) { + this.layers = layers; + } + + public void addLayer(CakeLayer layer) { + this.layers.add(layer); + } + + @Override + public String toString() { + return String.format("id=%s topping=%s layers=%s", id, topping, layers.toString()); + } +} diff --git a/layers/src/main/java/com/iluwatar/layers/CakeBakingException.java b/layers/src/main/java/com/iluwatar/layers/CakeBakingException.java new file mode 100644 index 000000000..4e79f34e1 --- /dev/null +++ b/layers/src/main/java/com/iluwatar/layers/CakeBakingException.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.layers; + +/** + * + * Custom exception used in cake baking + * + */ +public class CakeBakingException extends Exception { + + private static final long serialVersionUID = 1L; + + public CakeBakingException() {} + + public CakeBakingException(String message) { + super(message); + } +} diff --git a/layers/src/main/java/com/iluwatar/layers/CakeBakingService.java b/layers/src/main/java/com/iluwatar/layers/CakeBakingService.java new file mode 100644 index 000000000..1cb5b3574 --- /dev/null +++ b/layers/src/main/java/com/iluwatar/layers/CakeBakingService.java @@ -0,0 +1,63 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.layers; + +import java.util.List; + +/** + * + * Service for cake baking operations + * + */ +public interface CakeBakingService { + + /** + * Bakes new cake according to parameters + */ + void bakeNewCake(CakeInfo cakeInfo) throws CakeBakingException; + + /** + * Get all cakes + */ + List getAllCakes(); + + /** + * Store new cake topping + */ + void saveNewTopping(CakeToppingInfo toppingInfo); + + /** + * Get available cake toppings + */ + List getAvailableToppings(); + + /** + * Add new cake layer + */ + void saveNewLayer(CakeLayerInfo layerInfo); + + /** + * Get available cake layers + */ + List getAvailableLayers(); +} diff --git a/layers/src/main/java/com/iluwatar/layers/CakeBakingServiceImpl.java b/layers/src/main/java/com/iluwatar/layers/CakeBakingServiceImpl.java new file mode 100644 index 000000000..e8518b625 --- /dev/null +++ b/layers/src/main/java/com/iluwatar/layers/CakeBakingServiceImpl.java @@ -0,0 +1,175 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.layers; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import org.springframework.context.support.AbstractApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * + * Implementation of CakeBakingService + * + */ +@Service +@Transactional +public class CakeBakingServiceImpl implements CakeBakingService { + + private AbstractApplicationContext context; + + public CakeBakingServiceImpl() { + this.context = new ClassPathXmlApplicationContext("applicationContext.xml"); + } + + @Override + public void bakeNewCake(CakeInfo cakeInfo) throws CakeBakingException { + List allToppings = getAvailableToppingEntities(); + List matchingToppings = + allToppings.stream().filter(t -> t.getName().equals(cakeInfo.cakeToppingInfo.name)) + .collect(Collectors.toList()); + if (matchingToppings.isEmpty()) { + throw new CakeBakingException(String.format("Topping %s is not available", + cakeInfo.cakeToppingInfo.name)); + } + List allLayers = getAvailableLayerEntities(); + Set foundLayers = new HashSet<>(); + for (CakeLayerInfo info : cakeInfo.cakeLayerInfos) { + Optional found = + allLayers.stream().filter(layer -> layer.getName().equals(info.name)).findFirst(); + if (!found.isPresent()) { + throw new CakeBakingException(String.format("Layer %s is not available", info.name)); + } else { + foundLayers.add(found.get()); + } + } + CakeToppingDao toppingBean = context.getBean(CakeToppingDao.class); + CakeTopping topping = toppingBean.findOne(matchingToppings.iterator().next().getId()); + CakeDao cakeBean = context.getBean(CakeDao.class); + Cake cake = new Cake(); + cake.setTopping(topping); + cake.setLayers(foundLayers); + cakeBean.save(cake); + topping.setCake(cake); + toppingBean.save(topping); + CakeLayerDao layerBean = context.getBean(CakeLayerDao.class); + for (CakeLayer layer : foundLayers) { + layer.setCake(cake); + layerBean.save(layer); + } + } + + @Override + public void saveNewTopping(CakeToppingInfo toppingInfo) { + CakeToppingDao bean = context.getBean(CakeToppingDao.class); + bean.save(new CakeTopping(toppingInfo.name, toppingInfo.calories)); + } + + @Override + public void saveNewLayer(CakeLayerInfo layerInfo) { + CakeLayerDao bean = context.getBean(CakeLayerDao.class); + bean.save(new CakeLayer(layerInfo.name, layerInfo.calories)); + } + + private List getAvailableToppingEntities() { + CakeToppingDao bean = context.getBean(CakeToppingDao.class); + List result = new ArrayList<>(); + Iterator iterator = bean.findAll().iterator(); + while (iterator.hasNext()) { + CakeTopping topping = iterator.next(); + if (topping.getCake() == null) { + result.add(topping); + } + } + return result; + } + + @Override + public List getAvailableToppings() { + CakeToppingDao bean = context.getBean(CakeToppingDao.class); + List result = new ArrayList<>(); + Iterator iterator = bean.findAll().iterator(); + while (iterator.hasNext()) { + CakeTopping next = iterator.next(); + if (next.getCake() == null) { + result.add(new CakeToppingInfo(next.getId(), next.getName(), next.getCalories())); + } + } + return result; + } + + private List getAvailableLayerEntities() { + CakeLayerDao bean = context.getBean(CakeLayerDao.class); + List result = new ArrayList<>(); + Iterator iterator = bean.findAll().iterator(); + while (iterator.hasNext()) { + CakeLayer next = iterator.next(); + if (next.getCake() == null) { + result.add(next); + } + } + return result; + } + + @Override + public List getAvailableLayers() { + CakeLayerDao bean = context.getBean(CakeLayerDao.class); + List result = new ArrayList<>(); + Iterator iterator = bean.findAll().iterator(); + while (iterator.hasNext()) { + CakeLayer next = iterator.next(); + if (next.getCake() == null) { + result.add(new CakeLayerInfo(next.getId(), next.getName(), next.getCalories())); + } + } + return result; + } + + @Override + public List getAllCakes() { + CakeDao cakeBean = context.getBean(CakeDao.class); + List result = new ArrayList<>(); + Iterator iterator = cakeBean.findAll().iterator(); + while (iterator.hasNext()) { + Cake cake = iterator.next(); + CakeToppingInfo cakeToppingInfo = + new CakeToppingInfo(cake.getTopping().getId(), cake.getTopping().getName(), cake + .getTopping().getCalories()); + ArrayList cakeLayerInfos = new ArrayList<>(); + for (CakeLayer layer : cake.getLayers()) { + cakeLayerInfos.add(new CakeLayerInfo(layer.getId(), layer.getName(), layer.getCalories())); + } + CakeInfo cakeInfo = new CakeInfo(cake.getId(), cakeToppingInfo, cakeLayerInfos); + result.add(cakeInfo); + } + return result; + } +} diff --git a/layers/src/main/java/com/iluwatar/layers/CakeDao.java b/layers/src/main/java/com/iluwatar/layers/CakeDao.java new file mode 100644 index 000000000..15107b030 --- /dev/null +++ b/layers/src/main/java/com/iluwatar/layers/CakeDao.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.layers; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +/** + * + * CRUD repository for cakes + * + */ +@Repository +public interface CakeDao extends CrudRepository { + +} diff --git a/layers/src/main/java/com/iluwatar/layers/CakeInfo.java b/layers/src/main/java/com/iluwatar/layers/CakeInfo.java new file mode 100644 index 000000000..e71da5d30 --- /dev/null +++ b/layers/src/main/java/com/iluwatar/layers/CakeInfo.java @@ -0,0 +1,71 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.layers; + +import java.util.List; +import java.util.Optional; + +/** + * + * DTO for cakes + * + */ +public class CakeInfo { + + public final Optional id; + public final CakeToppingInfo cakeToppingInfo; + public final List cakeLayerInfos; + + /** + * Constructor + */ + public CakeInfo(Long id, CakeToppingInfo cakeToppingInfo, List cakeLayerInfos) { + this.id = Optional.of(id); + this.cakeToppingInfo = cakeToppingInfo; + this.cakeLayerInfos = cakeLayerInfos; + } + + /** + * Constructor + */ + public CakeInfo(CakeToppingInfo cakeToppingInfo, List cakeLayerInfos) { + this.id = Optional.empty(); + this.cakeToppingInfo = cakeToppingInfo; + this.cakeLayerInfos = cakeLayerInfos; + } + + /** + * Calculate calories + */ + public int calculateTotalCalories() { + int total = cakeToppingInfo != null ? cakeToppingInfo.calories : 0; + total += cakeLayerInfos.stream().mapToInt(c -> c.calories).sum(); + return total; + } + + @Override + public String toString() { + return String.format("CakeInfo id=%d topping=%s layers=%s totalCalories=%d", id.orElse(-1L), + cakeToppingInfo, cakeLayerInfos, calculateTotalCalories()); + } +} diff --git a/layers/src/main/java/com/iluwatar/layers/CakeLayer.java b/layers/src/main/java/com/iluwatar/layers/CakeLayer.java new file mode 100644 index 000000000..50632f1b0 --- /dev/null +++ b/layers/src/main/java/com/iluwatar/layers/CakeLayer.java @@ -0,0 +1,93 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.layers; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +/** + * + * CakeLayer entity + * + */ +@Entity +public class CakeLayer { + + @Id + @GeneratedValue + private Long id; + + private String name; + + private int calories; + + @ManyToOne(cascade = CascadeType.ALL) + private Cake cake; + + public CakeLayer() {} + + public CakeLayer(String name, int calories) { + this.setName(name); + this.setCalories(calories); + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public final void setName(String name) { + this.name = name; + } + + public int getCalories() { + return calories; + } + + public final void setCalories(int calories) { + this.calories = calories; + } + + @Override + public String toString() { + return String.format("id=%s name=%s calories=%d", id, name, calories); + } + + public Cake getCake() { + return cake; + } + + public void setCake(Cake cake) { + this.cake = cake; + } +} diff --git a/layers/src/main/java/com/iluwatar/layers/CakeLayerDao.java b/layers/src/main/java/com/iluwatar/layers/CakeLayerDao.java new file mode 100644 index 000000000..2eea55eaa --- /dev/null +++ b/layers/src/main/java/com/iluwatar/layers/CakeLayerDao.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.layers; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +/** + * + * CRUD repository for cake layers + * + */ +@Repository +public interface CakeLayerDao extends CrudRepository { + +} diff --git a/layers/src/main/java/com/iluwatar/layers/CakeLayerInfo.java b/layers/src/main/java/com/iluwatar/layers/CakeLayerInfo.java new file mode 100644 index 000000000..7f327cac3 --- /dev/null +++ b/layers/src/main/java/com/iluwatar/layers/CakeLayerInfo.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.layers; + +import java.util.Optional; + +/** + * + * DTO for cake layers + * + */ +public class CakeLayerInfo { + + public final Optional id; + public final String name; + public final int calories; + + /** + * Constructor + */ + public CakeLayerInfo(Long id, String name, int calories) { + this.id = Optional.of(id); + this.name = name; + this.calories = calories; + } + + /** + * Constructor + */ + public CakeLayerInfo(String name, int calories) { + this.id = Optional.empty(); + this.name = name; + this.calories = calories; + } + + @Override + public String toString() { + return String.format("CakeLayerInfo id=%d name=%s calories=%d", id.orElse(-1L), name, calories); + } +} diff --git a/layers/src/main/java/com/iluwatar/layers/CakeTopping.java b/layers/src/main/java/com/iluwatar/layers/CakeTopping.java new file mode 100644 index 000000000..7db86d0f6 --- /dev/null +++ b/layers/src/main/java/com/iluwatar/layers/CakeTopping.java @@ -0,0 +1,93 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.layers; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.OneToOne; + +/** + * + * CakeTopping entity + * + */ +@Entity +public class CakeTopping { + + @Id + @GeneratedValue + private Long id; + + private String name; + + private int calories; + + @OneToOne(cascade = CascadeType.ALL) + private Cake cake; + + public CakeTopping() {} + + public CakeTopping(String name, int calories) { + this.setName(name); + this.setCalories(calories); + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public final void setName(String name) { + this.name = name; + } + + public final int getCalories() { + return calories; + } + + public void setCalories(int calories) { + this.calories = calories; + } + + @Override + public String toString() { + return String.format("id=%s name=%s calories=%d", id, name, calories); + } + + public Cake getCake() { + return cake; + } + + public void setCake(Cake cake) { + this.cake = cake; + } +} diff --git a/layers/src/main/java/com/iluwatar/layers/CakeToppingDao.java b/layers/src/main/java/com/iluwatar/layers/CakeToppingDao.java new file mode 100644 index 000000000..7a7b32aa7 --- /dev/null +++ b/layers/src/main/java/com/iluwatar/layers/CakeToppingDao.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.layers; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +/** + * + * CRUD repository cake toppings + * + */ +@Repository +public interface CakeToppingDao extends CrudRepository { + +} diff --git a/layers/src/main/java/com/iluwatar/layers/CakeToppingInfo.java b/layers/src/main/java/com/iluwatar/layers/CakeToppingInfo.java new file mode 100644 index 000000000..9acb0526e --- /dev/null +++ b/layers/src/main/java/com/iluwatar/layers/CakeToppingInfo.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.layers; + +import java.util.Optional; + +/** + * + * DTO for cake toppings + * + */ +public class CakeToppingInfo { + + public final Optional id; + public final String name; + public final int calories; + + /** + * Constructor + */ + public CakeToppingInfo(Long id, String name, int calories) { + this.id = Optional.of(id); + this.name = name; + this.calories = calories; + } + + /** + * Constructor + */ + public CakeToppingInfo(String name, int calories) { + this.id = Optional.empty(); + this.name = name; + this.calories = calories; + } + + @Override + public String toString() { + return String.format("CakeToppingInfo id=%d name=%s calories=%d", id.orElse(-1L), name, calories); + } +} diff --git a/layers/src/main/java/com/iluwatar/layers/CakeViewImpl.java b/layers/src/main/java/com/iluwatar/layers/CakeViewImpl.java new file mode 100644 index 000000000..bc489e16e --- /dev/null +++ b/layers/src/main/java/com/iluwatar/layers/CakeViewImpl.java @@ -0,0 +1,41 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.layers; + +/** + * + * View implementation for displaying cakes + * + */ +public class CakeViewImpl implements View { + + private CakeBakingService cakeBakingService; + + public CakeViewImpl(CakeBakingService cakeBakingService) { + this.cakeBakingService = cakeBakingService; + } + + public void render() { + cakeBakingService.getAllCakes().stream().forEach(cake -> System.out.println(cake)); + } +} diff --git a/layers/src/main/java/com/iluwatar/layers/View.java b/layers/src/main/java/com/iluwatar/layers/View.java new file mode 100644 index 000000000..fa4f307ca --- /dev/null +++ b/layers/src/main/java/com/iluwatar/layers/View.java @@ -0,0 +1,34 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.layers; + +/** + * + * View interface + * + */ +public interface View { + + void render(); + +} diff --git a/layers/src/main/resources/META-INF/persistence.xml b/layers/src/main/resources/META-INF/persistence.xml new file mode 100644 index 000000000..32afb9fd4 --- /dev/null +++ b/layers/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,32 @@ + + + + + + + \ No newline at end of file diff --git a/layers/src/main/resources/applicationContext.xml b/layers/src/main/resources/applicationContext.xml new file mode 100644 index 000000000..e1ddc7427 --- /dev/null +++ b/layers/src/main/resources/applicationContext.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layers/src/test/java/com/iluwatar/layers/AppTest.java b/layers/src/test/java/com/iluwatar/layers/AppTest.java new file mode 100644 index 000000000..5a5374def --- /dev/null +++ b/layers/src/test/java/com/iluwatar/layers/AppTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.layers; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/layers/src/test/java/com/iluwatar/layers/CakeBakingExceptionTest.java b/layers/src/test/java/com/iluwatar/layers/CakeBakingExceptionTest.java new file mode 100644 index 000000000..1cca98472 --- /dev/null +++ b/layers/src/test/java/com/iluwatar/layers/CakeBakingExceptionTest.java @@ -0,0 +1,52 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.layers; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +/** + * Date: 12/15/15 - 7:57 PM + * + * @author Jeroen Meulemeester + */ +public class CakeBakingExceptionTest { + + @Test + public void testConstructor() throws Exception { + final CakeBakingException exception = new CakeBakingException(); + assertNull(exception.getMessage()); + assertNull(exception.getCause()); + } + + @Test + public void testConstructorWithMessage() throws Exception { + final String expectedMessage = "message"; + final CakeBakingException exception = new CakeBakingException(expectedMessage); + assertEquals(expectedMessage, exception.getMessage()); + assertNull(exception.getCause()); + } + +} diff --git a/layers/src/test/java/com/iluwatar/layers/CakeBakingServiceImplTest.java b/layers/src/test/java/com/iluwatar/layers/CakeBakingServiceImplTest.java new file mode 100644 index 000000000..e1c539cf2 --- /dev/null +++ b/layers/src/test/java/com/iluwatar/layers/CakeBakingServiceImplTest.java @@ -0,0 +1,181 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.layers; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Date: 12/15/15 - 9:55 PM + * + * @author Jeroen Meulemeester + */ +public class CakeBakingServiceImplTest { + + @Test + public void testLayers() throws CakeBakingException { + final CakeBakingServiceImpl service = new CakeBakingServiceImpl(); + + final List initialLayers = service.getAvailableLayers(); + assertNotNull(initialLayers); + assertTrue(initialLayers.isEmpty()); + + service.saveNewLayer(new CakeLayerInfo("Layer1", 1000)); + service.saveNewLayer(new CakeLayerInfo("Layer2", 2000)); + + final List availableLayers = service.getAvailableLayers(); + assertNotNull(availableLayers); + assertEquals(2, availableLayers.size()); + for (final CakeLayerInfo layer : availableLayers) { + assertNotNull(layer.id); + assertNotNull(layer.name); + assertNotNull(layer.toString()); + assertTrue(layer.calories > 0); + } + + } + + @Test + public void testToppings() throws CakeBakingException { + final CakeBakingServiceImpl service = new CakeBakingServiceImpl(); + + final List initialToppings = service.getAvailableToppings(); + assertNotNull(initialToppings); + assertTrue(initialToppings.isEmpty()); + + service.saveNewTopping(new CakeToppingInfo("Topping1", 1000)); + service.saveNewTopping(new CakeToppingInfo("Topping2", 2000)); + + final List availableToppings = service.getAvailableToppings(); + assertNotNull(availableToppings); + assertEquals(2, availableToppings.size()); + for (final CakeToppingInfo topping : availableToppings) { + assertNotNull(topping.id); + assertNotNull(topping.name); + assertNotNull(topping.toString()); + assertTrue(topping.calories > 0); + } + + } + + @Test + public void testBakeCakes() throws CakeBakingException { + final CakeBakingServiceImpl service = new CakeBakingServiceImpl(); + + final List initialCakes = service.getAllCakes(); + assertNotNull(initialCakes); + assertTrue(initialCakes.isEmpty()); + + final CakeToppingInfo topping1 = new CakeToppingInfo("Topping1", 1000); + final CakeToppingInfo topping2 = new CakeToppingInfo("Topping2", 2000); + service.saveNewTopping(topping1); + service.saveNewTopping(topping2); + + final CakeLayerInfo layer1 = new CakeLayerInfo("Layer1", 1000); + final CakeLayerInfo layer2 = new CakeLayerInfo("Layer2", 2000); + final CakeLayerInfo layer3 = new CakeLayerInfo("Layer3", 2000); + service.saveNewLayer(layer1); + service.saveNewLayer(layer2); + service.saveNewLayer(layer3); + + service.bakeNewCake(new CakeInfo(topping1, Arrays.asList(layer1, layer2))); + service.bakeNewCake(new CakeInfo(topping2, Collections.singletonList(layer3))); + + final List allCakes = service.getAllCakes(); + assertNotNull(allCakes); + assertEquals(2, allCakes.size()); + for (final CakeInfo cakeInfo : allCakes) { + assertNotNull(cakeInfo.id); + assertNotNull(cakeInfo.cakeToppingInfo); + assertNotNull(cakeInfo.cakeLayerInfos); + assertNotNull(cakeInfo.toString()); + assertFalse(cakeInfo.cakeLayerInfos.isEmpty()); + assertTrue(cakeInfo.calculateTotalCalories() > 0); + } + + } + + @Test(expected = CakeBakingException.class) + public void testBakeCakeMissingTopping() throws CakeBakingException { + final CakeBakingServiceImpl service = new CakeBakingServiceImpl(); + + final CakeLayerInfo layer1 = new CakeLayerInfo("Layer1", 1000); + final CakeLayerInfo layer2 = new CakeLayerInfo("Layer2", 2000); + service.saveNewLayer(layer1); + service.saveNewLayer(layer2); + + final CakeToppingInfo missingTopping = new CakeToppingInfo("Topping1", 1000); + service.bakeNewCake(new CakeInfo(missingTopping, Arrays.asList(layer1, layer2))); + } + + @Test(expected = CakeBakingException.class) + public void testBakeCakeMissingLayer() throws CakeBakingException { + final CakeBakingServiceImpl service = new CakeBakingServiceImpl(); + + final List initialCakes = service.getAllCakes(); + assertNotNull(initialCakes); + assertTrue(initialCakes.isEmpty()); + + final CakeToppingInfo topping1 = new CakeToppingInfo("Topping1", 1000); + service.saveNewTopping(topping1); + + final CakeLayerInfo layer1 = new CakeLayerInfo("Layer1", 1000); + service.saveNewLayer(layer1); + + final CakeLayerInfo missingLayer = new CakeLayerInfo("Layer2", 2000); + service.bakeNewCake(new CakeInfo(topping1, Arrays.asList(layer1, missingLayer))); + + } + + @Test(expected = CakeBakingException.class) + public void testBakeCakesUsedLayer() throws CakeBakingException { + final CakeBakingServiceImpl service = new CakeBakingServiceImpl(); + + final List initialCakes = service.getAllCakes(); + assertNotNull(initialCakes); + assertTrue(initialCakes.isEmpty()); + + final CakeToppingInfo topping1 = new CakeToppingInfo("Topping1", 1000); + final CakeToppingInfo topping2 = new CakeToppingInfo("Topping2", 2000); + service.saveNewTopping(topping1); + service.saveNewTopping(topping2); + + final CakeLayerInfo layer1 = new CakeLayerInfo("Layer1", 1000); + final CakeLayerInfo layer2 = new CakeLayerInfo("Layer2", 2000); + service.saveNewLayer(layer1); + service.saveNewLayer(layer2); + + service.bakeNewCake(new CakeInfo(topping1, Arrays.asList(layer1, layer2))); + service.bakeNewCake(new CakeInfo(topping2, Collections.singletonList(layer2))); + + } + +} diff --git a/layers/src/test/java/com/iluwatar/layers/CakeTest.java b/layers/src/test/java/com/iluwatar/layers/CakeTest.java new file mode 100644 index 000000000..6cd50106c --- /dev/null +++ b/layers/src/test/java/com/iluwatar/layers/CakeTest.java @@ -0,0 +1,119 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.layers; + +import org.junit.Test; + +import java.util.HashSet; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +/** + * Date: 12/15/15 - 8:02 PM + * + * @author Jeroen Meulemeester + */ +public class CakeTest { + + @Test + public void testSetId() { + final Cake cake = new Cake(); + assertNull(cake.getId()); + + final Long expectedId = Long.valueOf(1234L); + cake.setId(expectedId); + assertEquals(expectedId, cake.getId()); + } + + @Test + public void testSetTopping() { + final Cake cake = new Cake(); + assertNull(cake.getTopping()); + + final CakeTopping expectedTopping = new CakeTopping("DummyTopping", 1000); + cake.setTopping(expectedTopping); + assertEquals(expectedTopping, cake.getTopping()); + } + + @Test + public void testSetLayers() { + final Cake cake = new Cake(); + assertNotNull(cake.getLayers()); + assertTrue(cake.getLayers().isEmpty()); + + final Set expectedLayers = new HashSet<>(); + expectedLayers.add(new CakeLayer("layer1", 1000)); + expectedLayers.add(new CakeLayer("layer2", 2000)); + expectedLayers.add(new CakeLayer("layer3", 3000)); + + cake.setLayers(expectedLayers); + assertEquals(expectedLayers, cake.getLayers()); + } + + @Test + public void testAddLayer() { + final Cake cake = new Cake(); + assertNotNull(cake.getLayers()); + assertTrue(cake.getLayers().isEmpty()); + + final Set initialLayers = new HashSet<>(); + initialLayers.add(new CakeLayer("layer1", 1000)); + initialLayers.add(new CakeLayer("layer2", 2000)); + + cake.setLayers(initialLayers); + assertEquals(initialLayers, cake.getLayers()); + + final CakeLayer newLayer = new CakeLayer("layer3", 3000); + cake.addLayer(newLayer); + + final Set expectedLayers = new HashSet<>(); + expectedLayers.addAll(initialLayers); + expectedLayers.addAll(initialLayers); + expectedLayers.add(newLayer); + assertEquals(expectedLayers, cake.getLayers()); + } + + @Test + public void testToString() { + final CakeTopping topping = new CakeTopping("topping", 20); + topping.setId(2345L); + + final CakeLayer layer = new CakeLayer("layer", 100); + layer.setId(3456L); + + final Cake cake = new Cake(); + cake.setId(1234L); + cake.setTopping(topping); + cake.addLayer(layer); + + final String expected = "id=1234 topping=id=2345 name=topping calories=20 " + + "layers=[id=3456 name=layer calories=100]"; + assertEquals(expected, cake.toString()); + + } + +} diff --git a/layers/src/test/java/com/iluwatar/layers/CakeViewImplTest.java b/layers/src/test/java/com/iluwatar/layers/CakeViewImplTest.java new file mode 100644 index 000000000..a69aa23bb --- /dev/null +++ b/layers/src/test/java/com/iluwatar/layers/CakeViewImplTest.java @@ -0,0 +1,66 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.layers; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.Mockito.*; + +/** + * Date: 12/15/15 - 10:04 PM + * + * @author Jeroen Meulemeester + */ +public class CakeViewImplTest extends StdOutTest { + + /** + * Verify if the cake view renders the expected result + */ + @Test + public void testRender() { + + final List layers = new ArrayList<>(); + layers.add(new CakeLayerInfo("layer1", 1000)); + layers.add(new CakeLayerInfo("layer2", 2000)); + layers.add(new CakeLayerInfo("layer3", 3000)); + + final List cakes = new ArrayList<>(); + final CakeInfo cake = new CakeInfo(new CakeToppingInfo("topping", 1000), layers); + cakes.add(cake); + + final CakeBakingService bakingService = mock(CakeBakingService.class); + when(bakingService.getAllCakes()).thenReturn(cakes); + + final CakeViewImpl cakeView = new CakeViewImpl(bakingService); + + verifyZeroInteractions(getStdOutMock()); + + cakeView.render(); + verify(getStdOutMock(), times(1)).println(cake); + + } + +} diff --git a/layers/src/test/java/com/iluwatar/layers/StdOutTest.java b/layers/src/test/java/com/iluwatar/layers/StdOutTest.java new file mode 100644 index 000000000..3c94b178c --- /dev/null +++ b/layers/src/test/java/com/iluwatar/layers/StdOutTest.java @@ -0,0 +1,76 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.layers; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since the actions of the views don't have + * any influence on any other accessible objects, except for writing to std-out using {@link + * System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/lazy-loading/README.md b/lazy-loading/README.md new file mode 100644 index 000000000..d40061293 --- /dev/null +++ b/lazy-loading/README.md @@ -0,0 +1,33 @@ +--- +layout: pattern +title: Lazy Loading +folder: lazy-loading +permalink: /patterns/lazy-loading/ +categories: Other +tags: + - Java + - Difficulty-Beginner + - Idiom + - Performance +--- + +## Intent +Lazy loading is a design pattern commonly used to defer +initialization of an object until the point at which it is needed. It can +contribute to efficiency in the program's operation if properly and +appropriately used. + +![alt text](./etc/lazy-loading.png "Lazy Loading") + +## Applicability +Use the Lazy Loading idiom when + +* eager loading is expensive or the object to be loaded might not be needed at all + +## Real world examples + +* JPA annotations @OneToOne, @OneToMany, @ManyToOne, @ManyToMany and fetch = FetchType.LAZY + +## Credits + +* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) diff --git a/lazy-loading/pom.xml b/lazy-loading/pom.xml index 454a70a58..fe6d44c78 100644 --- a/lazy-loading/pom.xml +++ b/lazy-loading/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT lazy-loading diff --git a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/App.java b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/App.java new file mode 100644 index 000000000..7a658a8c6 --- /dev/null +++ b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/App.java @@ -0,0 +1,58 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.lazy.loading; + +/** + * + * Lazy loading idiom defers object creation until needed. + *

+ * This example shows different implementations of the pattern with increasing sophistication. + *

+ * Additional information and lazy loading flavours are described in + * http://martinfowler.com/eaaCatalog/lazyLoad.html + * + */ +public class App { + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + + // Simple lazy loader - not thread safe + HolderNaive holderNaive = new HolderNaive(); + Heavy heavy = holderNaive.getHeavy(); + System.out.println("heavy=" + heavy); + + // Thread safe lazy loader, but with heavy synchronization on each access + HolderThreadSafe holderThreadSafe = new HolderThreadSafe(); + Heavy another = holderThreadSafe.getHeavy(); + System.out.println("another=" + another); + + // The most efficient lazy loader utilizing Java 8 features + Java8Holder java8Holder = new Java8Holder(); + Heavy next = java8Holder.getHeavy(); + System.out.println("next=" + next); + } +} diff --git a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/Heavy.java b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/Heavy.java new file mode 100644 index 000000000..57e8e263e --- /dev/null +++ b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/Heavy.java @@ -0,0 +1,44 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.lazy.loading; + +/** + * + * Heavy objects are expensive to create. + * + */ +public class Heavy { + + /** + * Constructor + */ + public Heavy() { + System.out.println("Creating Heavy ..."); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("... Heavy created"); + } +} diff --git a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/HolderNaive.java b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/HolderNaive.java new file mode 100644 index 000000000..75c65d1b9 --- /dev/null +++ b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/HolderNaive.java @@ -0,0 +1,50 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.lazy.loading; + +/** + * + * Simple implementation of the lazy loading idiom. However, this is not thread safe. + * + */ +public class HolderNaive { + + private Heavy heavy; + + /** + * Constructor + */ + public HolderNaive() { + System.out.println("HolderNaive created"); + } + + /** + * Get heavy object + */ + public Heavy getHeavy() { + if (heavy == null) { + heavy = new Heavy(); + } + return heavy; + } +} diff --git a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/HolderThreadSafe.java b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/HolderThreadSafe.java new file mode 100644 index 000000000..c6e0fcfd9 --- /dev/null +++ b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/HolderThreadSafe.java @@ -0,0 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.lazy.loading; + +/** + * + * Same as HolderNaive but with added synchronization. This implementation is thread safe, but each + * {@link #getHeavy()} call costs additional synchronization overhead. + * + */ +public class HolderThreadSafe { + + private Heavy heavy; + + /** + * Constructor + */ + public HolderThreadSafe() { + System.out.println("HolderThreadSafe created"); + } + + /** + * Get heavy object + */ + public synchronized Heavy getHeavy() { + if (heavy == null) { + heavy = new Heavy(); + } + return heavy; + } +} diff --git a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/Java8Holder.java b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/Java8Holder.java new file mode 100644 index 000000000..e4ce394cc --- /dev/null +++ b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/Java8Holder.java @@ -0,0 +1,59 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.lazy.loading; + +import java.util.function.Supplier; + +/** + * + * This lazy loader is thread safe and more efficient than {@link HolderThreadSafe}. It utilizes + * Java 8 functional interface {@link Supplier} as {@link Heavy} factory. + * + */ +public class Java8Holder { + + private Supplier heavy = () -> createAndCacheHeavy(); + + public Java8Holder() { + System.out.println("Java8Holder created"); + } + + public Heavy getHeavy() { + return heavy.get(); + } + + private synchronized Heavy createAndCacheHeavy() { + class HeavyFactory implements Supplier { + private final Heavy heavyInstance = new Heavy(); + + @Override + public Heavy get() { + return heavyInstance; + } + } + if (!HeavyFactory.class.isInstance(heavy)) { + heavy = new HeavyFactory(); + } + return heavy.get(); + } +} diff --git a/lazy-loading/src/main/java/com/iluwatar/lazyloading/App.java b/lazy-loading/src/main/java/com/iluwatar/lazyloading/App.java deleted file mode 100644 index 608c39b5f..000000000 --- a/lazy-loading/src/main/java/com/iluwatar/lazyloading/App.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.iluwatar.lazyloading; - -/** - * - * Lazy loading idiom defers object creation until needed. - * - * This example shows different implementations of the pattern - * with increasing sophistication. - * - * Additional information and lazy loading flavours are described in - * http://martinfowler.com/eaaCatalog/lazyLoad.html - * - */ -public class App -{ - public static void main( String[] args ) { - - // Simple lazy loader - not thread safe - HolderNaive holderNaive = new HolderNaive(); - Heavy heavy = holderNaive.getHeavy(); - System.out.println("heavy=" + heavy); - - // Thread safe lazy loader, but with heavy synchronization on each access - HolderThreadSafe holderThreadSafe = new HolderThreadSafe(); - Heavy another = holderThreadSafe.getHeavy(); - System.out.println("another=" + another); - - // The most efficient lazy loader utilizing Java 8 features - Java8Holder java8Holder = new Java8Holder(); - Heavy next = java8Holder.getHeavy(); - System.out.println("next=" + next); - } -} diff --git a/lazy-loading/src/main/java/com/iluwatar/lazyloading/Heavy.java b/lazy-loading/src/main/java/com/iluwatar/lazyloading/Heavy.java deleted file mode 100644 index d87d4b5f7..000000000 --- a/lazy-loading/src/main/java/com/iluwatar/lazyloading/Heavy.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.iluwatar.lazyloading; - -/** - * - * Heavy objects are expensive to create. - * - */ -public class Heavy { - - public Heavy() { - System.out.println("Creating Heavy ..."); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - System.out.println("... Heavy created"); - } -} diff --git a/lazy-loading/src/main/java/com/iluwatar/lazyloading/HolderNaive.java b/lazy-loading/src/main/java/com/iluwatar/lazyloading/HolderNaive.java deleted file mode 100644 index 1abc6463a..000000000 --- a/lazy-loading/src/main/java/com/iluwatar/lazyloading/HolderNaive.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.iluwatar.lazyloading; - -/** - * - * Simple implementation of the lazy loading idiom. - * However, this is not thread safe. - * - */ -public class HolderNaive { - - private Heavy heavy; - - public HolderNaive() { - System.out.println("HolderNaive created"); - } - - public Heavy getHeavy() { - if (heavy == null) { - heavy = new Heavy(); - } - return heavy; - } -} diff --git a/lazy-loading/src/main/java/com/iluwatar/lazyloading/HolderThreadSafe.java b/lazy-loading/src/main/java/com/iluwatar/lazyloading/HolderThreadSafe.java deleted file mode 100644 index eff35c14a..000000000 --- a/lazy-loading/src/main/java/com/iluwatar/lazyloading/HolderThreadSafe.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.iluwatar.lazyloading; - -/** - * - * Same as HolderNaive but with added synchronization. - * This implementation is thread safe, but each {@link #getHeavy()} - * call costs additional synchronization overhead. - * - */ -public class HolderThreadSafe { - - private Heavy heavy; - - public HolderThreadSafe() { - System.out.println("HolderThreadSafe created"); - } - - public synchronized Heavy getHeavy() { - if (heavy == null) { - heavy = new Heavy(); - } - return heavy; - } -} diff --git a/lazy-loading/src/main/java/com/iluwatar/lazyloading/Java8Holder.java b/lazy-loading/src/main/java/com/iluwatar/lazyloading/Java8Holder.java deleted file mode 100644 index 6be5e374e..000000000 --- a/lazy-loading/src/main/java/com/iluwatar/lazyloading/Java8Holder.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.iluwatar.lazyloading; - -import java.util.function.Supplier; - -/** - * - * This lazy loader is thread safe and more efficient than HolderThreadSafe. - * It utilizes Java 8 functional interface Supplier as Heavy factory. - * - */ -public class Java8Holder { - - private Supplier heavy = () -> createAndCacheHeavy(); - - public Java8Holder() { - System.out.println("Java8Holder created"); - } - - public Heavy getHeavy() { - return heavy.get(); - } - - private synchronized Heavy createAndCacheHeavy() { - class HeavyFactory implements Supplier { - private final Heavy heavyInstance = new Heavy(); - @Override - public Heavy get() { return heavyInstance; } - } - if (!HeavyFactory.class.isInstance(heavy)) { - heavy = new HeavyFactory(); - } - return heavy.get(); - } -} diff --git a/lazy-loading/src/test/java/com/iluwatar/lazy/loading/AbstractHolderTest.java b/lazy-loading/src/test/java/com/iluwatar/lazy/loading/AbstractHolderTest.java new file mode 100644 index 000000000..3118847d9 --- /dev/null +++ b/lazy-loading/src/test/java/com/iluwatar/lazy/loading/AbstractHolderTest.java @@ -0,0 +1,63 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.lazy.loading; + +import org.junit.Test; + +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertSame; +import static junit.framework.TestCase.assertNull; + +/** + * Date: 12/19/15 - 11:58 AM + * + * @author Jeroen Meulemeester + */ +public abstract class AbstractHolderTest { + + /** + * Get the internal state of the holder value + * + * @return The internal value + */ + abstract Heavy getInternalHeavyValue() throws Exception; + + /** + * Request a lazy loaded {@link Heavy} object from the holder. + * + * @return The lazy loaded {@link Heavy} object + */ + abstract Heavy getHeavy() throws Exception; + + /** + * This test shows that the heavy field is not instantiated until the method getHeavy is called + */ + @Test(timeout = 3000) + public void testGetHeavy() throws Exception { + assertNull(getInternalHeavyValue()); + assertNotNull(getHeavy()); + assertNotNull(getInternalHeavyValue()); + assertSame(getHeavy(), getInternalHeavyValue()); + } + +} diff --git a/lazy-loading/src/test/java/com/iluwatar/lazy/loading/AppTest.java b/lazy-loading/src/test/java/com/iluwatar/lazy/loading/AppTest.java new file mode 100644 index 000000000..36cdedb6a --- /dev/null +++ b/lazy-loading/src/test/java/com/iluwatar/lazy/loading/AppTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.lazy.loading; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/lazy-loading/src/test/java/com/iluwatar/lazy/loading/HolderNaiveTest.java b/lazy-loading/src/test/java/com/iluwatar/lazy/loading/HolderNaiveTest.java new file mode 100644 index 000000000..a9c1b6afb --- /dev/null +++ b/lazy-loading/src/test/java/com/iluwatar/lazy/loading/HolderNaiveTest.java @@ -0,0 +1,48 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.lazy.loading; + +import java.lang.reflect.Field; + +/** + * Date: 12/19/15 - 12:05 PM + * + * @author Jeroen Meulemeester + */ +public class HolderNaiveTest extends AbstractHolderTest { + + private final HolderNaive holder = new HolderNaive(); + + @Override + Heavy getInternalHeavyValue() throws Exception { + final Field holderField = HolderNaive.class.getDeclaredField("heavy"); + holderField.setAccessible(true); + return (Heavy) holderField.get(this.holder); + } + + @Override + Heavy getHeavy() { + return holder.getHeavy(); + } + +} \ No newline at end of file diff --git a/lazy-loading/src/test/java/com/iluwatar/lazy/loading/HolderThreadSafeTest.java b/lazy-loading/src/test/java/com/iluwatar/lazy/loading/HolderThreadSafeTest.java new file mode 100644 index 000000000..3803a482e --- /dev/null +++ b/lazy-loading/src/test/java/com/iluwatar/lazy/loading/HolderThreadSafeTest.java @@ -0,0 +1,48 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.lazy.loading; + +import java.lang.reflect.Field; + +/** + * Date: 12/19/15 - 12:19 PM + * + * @author Jeroen Meulemeester + */ +public class HolderThreadSafeTest extends AbstractHolderTest { + + private final HolderThreadSafe holder = new HolderThreadSafe(); + + @Override + Heavy getInternalHeavyValue() throws Exception { + final Field holderField = HolderThreadSafe.class.getDeclaredField("heavy"); + holderField.setAccessible(true); + return (Heavy) holderField.get(this.holder); + } + + @Override + Heavy getHeavy() throws Exception { + return this.holder.getHeavy(); + } + +} \ No newline at end of file diff --git a/lazy-loading/src/test/java/com/iluwatar/lazy/loading/Java8HolderTest.java b/lazy-loading/src/test/java/com/iluwatar/lazy/loading/Java8HolderTest.java new file mode 100644 index 000000000..08a3995f9 --- /dev/null +++ b/lazy-loading/src/test/java/com/iluwatar/lazy/loading/Java8HolderTest.java @@ -0,0 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.lazy.loading; + +import java.lang.reflect.Field; +import java.util.function.Supplier; + +/** + * Date: 12/19/15 - 12:27 PM + * + * @author Jeroen Meulemeester + */ +public class Java8HolderTest extends AbstractHolderTest { + + private final Java8Holder holder = new Java8Holder(); + + + @Override + Heavy getInternalHeavyValue() throws Exception { + final Field holderField = Java8Holder.class.getDeclaredField("heavy"); + holderField.setAccessible(true); + + final Supplier supplier = (Supplier) holderField.get(this.holder); + final Class supplierClass = supplier.getClass(); + + // This is a little fishy, but I don't know another way to test this: + // The lazy holder is at first a lambda, but gets replaced with a new supplier after loading ... + if (supplierClass.isLocalClass()) { + final Field instanceField = supplierClass.getDeclaredField("heavyInstance"); + instanceField.setAccessible(true); + return (Heavy) instanceField.get(supplier); + } else { + return null; + } + } + + @Override + Heavy getHeavy() throws Exception { + return holder.getHeavy(); + } + +} \ No newline at end of file diff --git a/lazy-loading/src/test/java/com/iluwatar/lazyloading/AppTest.java b/lazy-loading/src/test/java/com/iluwatar/lazyloading/AppTest.java deleted file mode 100644 index 142da8f2e..000000000 --- a/lazy-loading/src/test/java/com/iluwatar/lazyloading/AppTest.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.iluwatar.lazyloading; - -import org.junit.Test; - -import com.iluwatar.lazyloading.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} diff --git a/mediator/README.md b/mediator/README.md new file mode 100644 index 000000000..c7a0478c8 --- /dev/null +++ b/mediator/README.md @@ -0,0 +1,29 @@ +--- +layout: pattern +title: Mediator +folder: mediator +permalink: /patterns/mediator/ +categories: Behavioral +tags: + - Java + - Gang Of Four + - Difficulty-Intermediate +--- + +## Intent +Define an object that encapsulates how a set of objects interact. +Mediator promotes loose coupling by keeping objects from referring to each +other explicitly, and it lets you vary their interaction independently. + +![alt text](./etc/mediator_1.png "Mediator") + +## Applicability +Use the Mediator pattern when + +* a set of objects communicate in well-defined but complex ways. The resulting interdependencies are unstructured and difficult to understand +* reusing an object is difficult because it refers to and communicates with many other objects +* a behavior that's distributed between several classes should be customizable without a lot of subclassing + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/mediator/pom.xml b/mediator/pom.xml index 95ad4d78f..a6cdd028e 100644 --- a/mediator/pom.xml +++ b/mediator/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT mediator @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/mediator/src/main/java/com/iluwatar/mediator/Action.java b/mediator/src/main/java/com/iluwatar/mediator/Action.java index a5b466342..b2dc25ed3 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/Action.java +++ b/mediator/src/main/java/com/iluwatar/mediator/Action.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.mediator; /** @@ -7,25 +29,23 @@ package com.iluwatar.mediator; */ public enum Action { - HUNT("hunted a rabbit", "arrives for dinner"), - TALE("tells a tale", "comes to listen"), - GOLD("found gold", "takes his share of the gold"), - ENEMY("spotted enemies", "runs for cover"), - NONE("", ""); + HUNT("hunted a rabbit", "arrives for dinner"), TALE("tells a tale", "comes to listen"), GOLD( + "found gold", "takes his share of the gold"), ENEMY("spotted enemies", "runs for cover"), NONE( + "", ""); - private String title; - private String description; + private String title; + private String description; - Action(String title, String description) { - this.title = title; - this.description = description; - } + Action(String title, String description) { + this.title = title; + this.description = description; + } - public String getDescription() { - return description; - } + public String getDescription() { + return description; + } - public String toString() { - return title; - } + public String toString() { + return title; + } } diff --git a/mediator/src/main/java/com/iluwatar/mediator/App.java b/mediator/src/main/java/com/iluwatar/mediator/App.java index 6c5233d26..57aec35d2 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/App.java +++ b/mediator/src/main/java/com/iluwatar/mediator/App.java @@ -1,33 +1,76 @@ -package com.iluwatar.mediator; - -/** - * - * Mediator encapsulates how a set of objects (PartyMember) interact. Instead of - * referring to each other directly they use a mediator (Party) interface. - * - */ -public class App { - - public static void main(String[] args) { - - // create party and members - Party party = new PartyImpl(); - Hobbit hobbit = new Hobbit(); - Wizard wizard = new Wizard(); - Rogue rogue = new Rogue(); - Hunter hunter = new Hunter(); - - // add party members - party.addMember(hobbit); - party.addMember(wizard); - party.addMember(rogue); - party.addMember(hunter); - - // perform actions -> the other party members - // are notified by the party - hobbit.act(Action.ENEMY); - wizard.act(Action.TALE); - rogue.act(Action.GOLD); - hunter.act(Action.HUNT); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.mediator; + +/** + * + * The Mediator pattern defines an object that encapsulates how a set of objects interact. This + * pattern is considered to be a behavioral pattern due to the way it can alter the program's + * running behavior. + *

+ * Usually a program is made up of a large number of classes. So the logic and computation is + * distributed among these classes. However, as more classes are developed in a program, especially + * during maintenance and/or refactoring, the problem of communication between these classes may + * become more complex. This makes the program harder to read and maintain. Furthermore, it can + * become difficult to change the program, since any change may affect code in several other + * classes. + *

+ * With the Mediator pattern, communication between objects is encapsulated with a mediator object. + * Objects no longer communicate directly with each other, but instead communicate through the + * mediator. This reduces the dependencies between communicating objects, thereby lowering the + * coupling. + *

+ * In this example the mediator encapsulates how a set of objects ({@link PartyMember}) interact. + * Instead of referring to each other directly they use the mediator ({@link Party}) interface. + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + + // create party and members + Party party = new PartyImpl(); + Hobbit hobbit = new Hobbit(); + Wizard wizard = new Wizard(); + Rogue rogue = new Rogue(); + Hunter hunter = new Hunter(); + + // add party members + party.addMember(hobbit); + party.addMember(wizard); + party.addMember(rogue); + party.addMember(hunter); + + // perform actions -> the other party members + // are notified by the party + hobbit.act(Action.ENEMY); + wizard.act(Action.TALE); + rogue.act(Action.GOLD); + hunter.act(Action.HUNT); + } +} diff --git a/mediator/src/main/java/com/iluwatar/mediator/Hobbit.java b/mediator/src/main/java/com/iluwatar/mediator/Hobbit.java index 5981a34a8..41ed5f5bd 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/Hobbit.java +++ b/mediator/src/main/java/com/iluwatar/mediator/Hobbit.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.mediator; /** @@ -7,9 +29,9 @@ package com.iluwatar.mediator; */ public class Hobbit extends PartyMemberBase { - @Override - public String toString() { - return "Hobbit"; - } + @Override + public String toString() { + return "Hobbit"; + } } diff --git a/mediator/src/main/java/com/iluwatar/mediator/Hunter.java b/mediator/src/main/java/com/iluwatar/mediator/Hunter.java index 1487c836e..0ff2a550e 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/Hunter.java +++ b/mediator/src/main/java/com/iluwatar/mediator/Hunter.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.mediator; /** @@ -7,9 +29,8 @@ package com.iluwatar.mediator; */ public class Hunter extends PartyMemberBase { - @Override - public String toString() { - return "Hunter"; - } - + @Override + public String toString() { + return "Hunter"; + } } diff --git a/mediator/src/main/java/com/iluwatar/mediator/Party.java b/mediator/src/main/java/com/iluwatar/mediator/Party.java index 9328cdaa2..c3c2f1666 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/Party.java +++ b/mediator/src/main/java/com/iluwatar/mediator/Party.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.mediator; /** @@ -7,8 +29,8 @@ package com.iluwatar.mediator; */ public interface Party { - void addMember(PartyMember member); + void addMember(PartyMember member); - void act(PartyMember actor, Action action); + void act(PartyMember actor, Action action); } diff --git a/mediator/src/main/java/com/iluwatar/mediator/PartyImpl.java b/mediator/src/main/java/com/iluwatar/mediator/PartyImpl.java index 5548f5163..f45c2869a 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/PartyImpl.java +++ b/mediator/src/main/java/com/iluwatar/mediator/PartyImpl.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.mediator; import java.util.ArrayList; @@ -10,24 +32,24 @@ import java.util.List; */ public class PartyImpl implements Party { - private final List members; + private final List members; - public PartyImpl() { - members = new ArrayList<>(); - } + public PartyImpl() { + members = new ArrayList<>(); + } - @Override - public void act(PartyMember actor, Action action) { - for (PartyMember member : members) { - if (member != actor) { - member.partyAction(action); - } - } - } + @Override + public void act(PartyMember actor, Action action) { + for (PartyMember member : members) { + if (!member.equals(actor)) { + member.partyAction(action); + } + } + } - @Override - public void addMember(PartyMember member) { - members.add(member); - member.joinedParty(this); - } + @Override + public void addMember(PartyMember member) { + members.add(member); + member.joinedParty(this); + } } diff --git a/mediator/src/main/java/com/iluwatar/mediator/PartyMember.java b/mediator/src/main/java/com/iluwatar/mediator/PartyMember.java index 4dbfd450b..1406ca889 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/PartyMember.java +++ b/mediator/src/main/java/com/iluwatar/mediator/PartyMember.java @@ -1,15 +1,37 @@ -package com.iluwatar.mediator; - -/** - * - * Interface for party members interacting with Party. - * - */ -public interface PartyMember { - - void joinedParty(Party party); - - void partyAction(Action action); - - void act(Action action); -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.mediator; + +/** + * + * Interface for party members interacting with {@link Party}. + * + */ +public interface PartyMember { + + void joinedParty(Party party); + + void partyAction(Action action); + + void act(Action action); +} diff --git a/mediator/src/main/java/com/iluwatar/mediator/PartyMemberBase.java b/mediator/src/main/java/com/iluwatar/mediator/PartyMemberBase.java index 7a151c355..7d3ca3dbf 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/PartyMemberBase.java +++ b/mediator/src/main/java/com/iluwatar/mediator/PartyMemberBase.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.mediator; /** @@ -7,28 +29,28 @@ package com.iluwatar.mediator; */ public abstract class PartyMemberBase implements PartyMember { - protected Party party; + protected Party party; - @Override - public void joinedParty(Party party) { - System.out.println(this + " joins the party"); - this.party = party; - } + @Override + public void joinedParty(Party party) { + System.out.println(this + " joins the party"); + this.party = party; + } - @Override - public void partyAction(Action action) { - System.out.println(this + " " + action.getDescription()); - } + @Override + public void partyAction(Action action) { + System.out.println(this + " " + action.getDescription()); + } - @Override - public void act(Action action) { - if (party != null) { - System.out.println(this + " " + action.toString()); - party.act(this, action); - } - } + @Override + public void act(Action action) { + if (party != null) { + System.out.println(this + " " + action.toString()); + party.act(this, action); + } + } - @Override - public abstract String toString(); + @Override + public abstract String toString(); } diff --git a/mediator/src/main/java/com/iluwatar/mediator/Rogue.java b/mediator/src/main/java/com/iluwatar/mediator/Rogue.java index 69974066f..b15d81d51 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/Rogue.java +++ b/mediator/src/main/java/com/iluwatar/mediator/Rogue.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.mediator; /** @@ -7,9 +29,9 @@ package com.iluwatar.mediator; */ public class Rogue extends PartyMemberBase { - @Override - public String toString() { - return "Rogue"; - } + @Override + public String toString() { + return "Rogue"; + } } diff --git a/mediator/src/main/java/com/iluwatar/mediator/Wizard.java b/mediator/src/main/java/com/iluwatar/mediator/Wizard.java index d4d2594d7..6f9d4433b 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/Wizard.java +++ b/mediator/src/main/java/com/iluwatar/mediator/Wizard.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.mediator; /** @@ -7,9 +29,9 @@ package com.iluwatar.mediator; */ public class Wizard extends PartyMemberBase { - @Override - public String toString() { - return "Wizard"; - } + @Override + public String toString() { + return "Wizard"; + } } diff --git a/mediator/src/test/java/com/iluwatar/mediator/AppTest.java b/mediator/src/test/java/com/iluwatar/mediator/AppTest.java index 79bd9478d..7691e11b0 100644 --- a/mediator/src/test/java/com/iluwatar/mediator/AppTest.java +++ b/mediator/src/test/java/com/iluwatar/mediator/AppTest.java @@ -1,14 +1,39 @@ -package com.iluwatar.mediator; - -import org.junit.Test; - -import com.iluwatar.mediator.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.mediator; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/mediator/src/test/java/com/iluwatar/mediator/PartyImplTest.java b/mediator/src/test/java/com/iluwatar/mediator/PartyImplTest.java new file mode 100644 index 000000000..7e9181fd7 --- /dev/null +++ b/mediator/src/test/java/com/iluwatar/mediator/PartyImplTest.java @@ -0,0 +1,63 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.mediator; + +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/19/15 - 10:00 PM + * + * @author Jeroen Meulemeester + */ +public class PartyImplTest { + + /** + * Verify if a member is notified when it's joining a party. Generate an action and see if the + * other member gets it. Also check members don't get their own actions. + */ + @Test + public void testPartyAction() { + final PartyMember partyMember1 = mock(PartyMember.class); + final PartyMember partyMember2 = mock(PartyMember.class); + + final PartyImpl party = new PartyImpl(); + party.addMember(partyMember1); + party.addMember(partyMember2); + + verify(partyMember1).joinedParty(party); + verify(partyMember2).joinedParty(party); + + party.act(partyMember1, Action.GOLD); + verifyZeroInteractions(partyMember1); + verify(partyMember2).partyAction(Action.GOLD); + + verifyNoMoreInteractions(partyMember1, partyMember2); + + } + +} diff --git a/mediator/src/test/java/com/iluwatar/mediator/PartyMemberTest.java b/mediator/src/test/java/com/iluwatar/mediator/PartyMemberTest.java new file mode 100644 index 000000000..0bf43f9cd --- /dev/null +++ b/mediator/src/test/java/com/iluwatar/mediator/PartyMemberTest.java @@ -0,0 +1,150 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.mediator; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.PrintStream; +import java.util.Arrays; +import java.util.Collection; +import java.util.function.Supplier; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/19/15 - 10:13 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class PartyMemberTest { + + @Parameterized.Parameters + public static Collection[]> data() { + return Arrays.asList( + new Supplier[]{Hobbit::new}, + new Supplier[]{Hunter::new}, + new Supplier[]{Rogue::new}, + new Supplier[]{Wizard::new} + ); + } + + /** + * The mocked standard out {@link PrintStream}, required since some actions on a {@link + * PartyMember} have any influence on any other accessible objects, except for writing to std-out + * using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * The factory, used to create a new instance of the tested party member + */ + private final Supplier memberSupplier; + + /** + * Create a new test instance, using the given {@link PartyMember} factory + * + * @param memberSupplier The party member factory + */ + public PartyMemberTest(final Supplier memberSupplier) { + this.memberSupplier = memberSupplier; + } + + /** + * Verify if a party action triggers the correct output to the std-Out + */ + @Test + public void testPartyAction() { + final PartyMember member = this.memberSupplier.get(); + + for (final Action action : Action.values()) { + member.partyAction(action); + verify(this.stdOutMock).println(member.toString() + " " + action.getDescription()); + } + + verifyNoMoreInteractions(this.stdOutMock); + } + + /** + * Verify if a member action triggers the expected interactions with the party class + */ + @Test + public void testAct() { + final PartyMember member = this.memberSupplier.get(); + + member.act(Action.GOLD); + verifyZeroInteractions(this.stdOutMock); + + final Party party = mock(Party.class); + member.joinedParty(party); + verify(this.stdOutMock).println(member.toString() + " joins the party"); + + for (final Action action : Action.values()) { + member.act(action); + verify(this.stdOutMock).println(member.toString() + " " + action.toString()); + verify(party).act(member, action); + } + + verifyNoMoreInteractions(party, this.stdOutMock); + } + + /** + * Verify if {@link PartyMember#toString()} generate the expected output + */ + @Test + public void testToString() throws Exception { + final PartyMember member = this.memberSupplier.get(); + final Class memberClass = member.getClass(); + assertEquals(memberClass.getSimpleName(), member.toString()); + } + +} diff --git a/memento/README.md b/memento/README.md new file mode 100644 index 000000000..463b5fec0 --- /dev/null +++ b/memento/README.md @@ -0,0 +1,34 @@ +--- +layout: pattern +title: Memento +folder: memento +permalink: /patterns/memento/ +categories: Behavioral +tags: + - Java + - Gang Of Four + - Difficulty-Intermediate +--- + +## Also known as +Token + +## Intent +Without violating encapsulation, capture and externalize an +object's internal state so that the object can be restored to this state later. + +![alt text](./etc/memento.png "Memento") + +## Applicability +Use the Memento pattern when + +* a snapshot of an object's state must be saved so that it can be restored to that state later, and +* a direct interface to obtaining the state would expose implementation details and break the object's encapsulation + +## Real world examples + +* [java.util.Date](http://docs.oracle.com/javase/8/docs/api/java/util/Date.html) + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/memento/pom.xml b/memento/pom.xml index db012477b..a320f186c 100644 --- a/memento/pom.xml +++ b/memento/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT memento diff --git a/memento/src/main/java/com/iluwatar/memento/App.java b/memento/src/main/java/com/iluwatar/memento/App.java index 9411cb4fd..e1ee349b1 100644 --- a/memento/src/main/java/com/iluwatar/memento/App.java +++ b/memento/src/main/java/com/iluwatar/memento/App.java @@ -1,36 +1,73 @@ -package com.iluwatar.memento; - -import java.util.Stack; - -/** - * - * Memento pattern is for storing and restoring object state. The object (Star) - * gives out a "memento" (StarMemento) that contains the state of the object. - * Later on the memento can be set back to the object restoring the state. - * - */ -public class App { - - public static void main(String[] args) { - Stack states = new Stack<>(); - - Star star = new Star(StarType.SUN, 10000000, 500000); - System.out.println(star); - states.add(star.getMemento()); - star.timePasses(); - System.out.println(star); - states.add(star.getMemento()); - star.timePasses(); - System.out.println(star); - states.add(star.getMemento()); - star.timePasses(); - System.out.println(star); - states.add(star.getMemento()); - star.timePasses(); - System.out.println(star); - while (states.size() > 0) { - star.setMemento(states.pop()); - System.out.println(star); - } - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.memento; + +import java.util.Stack; + +/** + * + * The Memento pattern is a software design pattern that provides the ability to restore an object + * to its previous state (undo via rollback). + *

+ * The Memento pattern is implemented with three objects: the originator, a caretaker and a memento. + * The originator is some object that has an internal state. The caretaker is going to do something + * to the originator, but wants to be able to undo the change. The caretaker first asks the + * originator for a memento object. Then it does whatever operation (or sequence of operations) it + * was going to do. To roll back to the state before the operations, it returns the memento object + * to the originator. The memento object itself is an opaque object (one which the caretaker cannot, + * or should not, change). When using this pattern, care should be taken if the originator may + * change other objects or resources - the memento pattern operates on a single object. + *

+ * In this example the object ({@link Star}) gives out a "memento" ({@link StarMemento}) that + * contains the state of the object. Later on the memento can be set back to the object restoring + * the state. + * + */ +public class App { + + /** + * Program entry point + */ + public static void main(String[] args) { + Stack states = new Stack<>(); + + Star star = new Star(StarType.SUN, 10000000, 500000); + System.out.println(star); + states.add(star.getMemento()); + star.timePasses(); + System.out.println(star); + states.add(star.getMemento()); + star.timePasses(); + System.out.println(star); + states.add(star.getMemento()); + star.timePasses(); + System.out.println(star); + states.add(star.getMemento()); + star.timePasses(); + System.out.println(star); + while (states.size() > 0) { + star.setMemento(states.pop()); + System.out.println(star); + } + } +} diff --git a/memento/src/main/java/com/iluwatar/memento/Star.java b/memento/src/main/java/com/iluwatar/memento/Star.java index 43b5c5c3c..d2df823a0 100644 --- a/memento/src/main/java/com/iluwatar/memento/Star.java +++ b/memento/src/main/java/com/iluwatar/memento/Star.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.memento; /** @@ -7,99 +29,104 @@ package com.iluwatar.memento; */ public class Star { - private StarType type; - private int ageYears; - private int massTons; + private StarType type; + private int ageYears; + private int massTons; - public Star(StarType startType, int startAge, int startMass) { - this.type = startType; - this.ageYears = startAge; - this.massTons = startMass; - } + /** + * Constructor + */ + public Star(StarType startType, int startAge, int startMass) { + this.type = startType; + this.ageYears = startAge; + this.massTons = startMass; + } - public void timePasses() { - ageYears *= 2; - massTons *= 8; - switch (type) { - case RED_GIANT: - type = StarType.WHITE_DWARF; - break; - case SUN: - type = StarType.RED_GIANT; - break; - case SUPERNOVA: - type = StarType.DEAD; - break; - case WHITE_DWARF: - type = StarType.SUPERNOVA; - break; - case DEAD: - ageYears *= 2; - massTons = 0; - break; - default: - break; - } - } + /** + * Makes time pass for the star + */ + public void timePasses() { + ageYears *= 2; + massTons *= 8; + switch (type) { + case RED_GIANT: + type = StarType.WHITE_DWARF; + break; + case SUN: + type = StarType.RED_GIANT; + break; + case SUPERNOVA: + type = StarType.DEAD; + break; + case WHITE_DWARF: + type = StarType.SUPERNOVA; + break; + case DEAD: + ageYears *= 2; + massTons = 0; + break; + default: + break; + } + } - StarMemento getMemento() { + StarMemento getMemento() { - StarMementoInternal state = new StarMementoInternal(); - state.setAgeYears(ageYears); - state.setMassTons(massTons); - state.setType(type); - return state; + StarMementoInternal state = new StarMementoInternal(); + state.setAgeYears(ageYears); + state.setMassTons(massTons); + state.setType(type); + return state; - } + } - void setMemento(StarMemento memento) { + void setMemento(StarMemento memento) { - StarMementoInternal state = (StarMementoInternal) memento; - this.type = state.getType(); - this.ageYears = state.getAgeYears(); - this.massTons = state.getMassTons(); + StarMementoInternal state = (StarMementoInternal) memento; + this.type = state.getType(); + this.ageYears = state.getAgeYears(); + this.massTons = state.getMassTons(); - } + } - @Override - public String toString() { - return String.format("%s age: %d years mass: %d tons", type.toString(), - ageYears, massTons); - } - - /** - * - * StarMemento implementation - * - */ - private static class StarMementoInternal implements StarMemento { + @Override + public String toString() { + return String.format("%s age: %d years mass: %d tons", type.toString(), ageYears, massTons); + } - private StarType type; - private int ageYears; - private int massTons; + /** + * + * StarMemento implementation + * + */ + private static class StarMementoInternal implements StarMemento { - public StarType getType() { - return type; - } + private StarType type; + private int ageYears; + private int massTons; - public void setType(StarType type) { - this.type = type; - } + public StarType getType() { + return type; + } - public int getAgeYears() { - return ageYears; - } + public void setType(StarType type) { + this.type = type; + } - public void setAgeYears(int ageYears) { - this.ageYears = ageYears; - } + public int getAgeYears() { + return ageYears; + } - public int getMassTons() { - return massTons; - } + public void setAgeYears(int ageYears) { + this.ageYears = ageYears; + } - public void setMassTons(int massTons) { - this.massTons = massTons; - } - } + public int getMassTons() { + return massTons; + } + + public void setMassTons(int massTons) { + this.massTons = massTons; + } + } } diff --git a/memento/src/main/java/com/iluwatar/memento/StarMemento.java b/memento/src/main/java/com/iluwatar/memento/StarMemento.java index a60f1f058..5f66b2939 100644 --- a/memento/src/main/java/com/iluwatar/memento/StarMemento.java +++ b/memento/src/main/java/com/iluwatar/memento/StarMemento.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.memento; /** diff --git a/memento/src/main/java/com/iluwatar/memento/StarType.java b/memento/src/main/java/com/iluwatar/memento/StarType.java index 442025525..ffd2f1adb 100644 --- a/memento/src/main/java/com/iluwatar/memento/StarType.java +++ b/memento/src/main/java/com/iluwatar/memento/StarType.java @@ -1,17 +1,45 @@ -package com.iluwatar.memento; - -public enum StarType { - - SUN("sun"), RED_GIANT("red giant"), WHITE_DWARF("white dwarf"), SUPERNOVA("supernova"), DEAD("dead star"), UNDEFINED(""); - - private String title; - - StarType(String title) { - this.title = title; - } - - @Override - public String toString() { - return title; - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.memento; + +/** + * + * StarType enumeration + * + */ +public enum StarType { + + SUN("sun"), RED_GIANT("red giant"), WHITE_DWARF("white dwarf"), SUPERNOVA("supernova"), DEAD( + "dead star"), UNDEFINED(""); + + private String title; + + StarType(String title) { + this.title = title; + } + + @Override + public String toString() { + return title; + } +} diff --git a/memento/src/test/java/com/iluwatar/memento/AppTest.java b/memento/src/test/java/com/iluwatar/memento/AppTest.java index 72bd5946d..cfdce9471 100644 --- a/memento/src/test/java/com/iluwatar/memento/AppTest.java +++ b/memento/src/test/java/com/iluwatar/memento/AppTest.java @@ -1,14 +1,39 @@ -package com.iluwatar.memento; - -import org.junit.Test; - -import com.iluwatar.memento.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.memento; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/memento/src/test/java/com/iluwatar/memento/StarTest.java b/memento/src/test/java/com/iluwatar/memento/StarTest.java new file mode 100644 index 000000000..8f81fcfa7 --- /dev/null +++ b/memento/src/test/java/com/iluwatar/memento/StarTest.java @@ -0,0 +1,97 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.memento; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Date: 12/20/15 - 10:08 AM + * + * @author Jeroen Meulemeester + */ +public class StarTest { + + /** + * Verify the stages of a dying sun, without going back in time + */ + @Test + public void testTimePasses() { + final Star star = new Star(StarType.SUN, 1, 2); + assertEquals("sun age: 1 years mass: 2 tons", star.toString()); + + star.timePasses(); + assertEquals("red giant age: 2 years mass: 16 tons", star.toString()); + + star.timePasses(); + assertEquals("white dwarf age: 4 years mass: 128 tons", star.toString()); + + star.timePasses(); + assertEquals("supernova age: 8 years mass: 1024 tons", star.toString()); + + star.timePasses(); + assertEquals("dead star age: 16 years mass: 8192 tons", star.toString()); + + star.timePasses(); + assertEquals("dead star age: 64 years mass: 0 tons", star.toString()); + + star.timePasses(); + assertEquals("dead star age: 256 years mass: 0 tons", star.toString()); + } + + /** + * Verify some stage of a dying sun, but go back in time to test the memento + */ + @Test + public void testSetMemento() { + final Star star = new Star(StarType.SUN, 1, 2); + final StarMemento firstMemento = star.getMemento(); + assertEquals("sun age: 1 years mass: 2 tons", star.toString()); + + star.timePasses(); + final StarMemento secondMemento = star.getMemento(); + assertEquals("red giant age: 2 years mass: 16 tons", star.toString()); + + star.timePasses(); + final StarMemento thirdMemento = star.getMemento(); + assertEquals("white dwarf age: 4 years mass: 128 tons", star.toString()); + + star.timePasses(); + assertEquals("supernova age: 8 years mass: 1024 tons", star.toString()); + + star.setMemento(thirdMemento); + assertEquals("white dwarf age: 4 years mass: 128 tons", star.toString()); + + star.timePasses(); + assertEquals("supernova age: 8 years mass: 1024 tons", star.toString()); + + star.setMemento(secondMemento); + assertEquals("red giant age: 2 years mass: 16 tons", star.toString()); + + star.setMemento(firstMemento); + assertEquals("sun age: 1 years mass: 2 tons", star.toString()); + + } + +} diff --git a/message-channel/.gitignore b/message-channel/.gitignore new file mode 100644 index 000000000..b83d22266 --- /dev/null +++ b/message-channel/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/message-channel/README.md b/message-channel/README.md new file mode 100644 index 000000000..8aebd0157 --- /dev/null +++ b/message-channel/README.md @@ -0,0 +1,26 @@ +--- +layout: pattern +title: Message Channel +folder: message-channel +permalink: /patterns/message-channel/ +categories: Integration +tags: + - Java + - EIP + - Apache Camel™ +--- + +## Intent +When two applications communicate using a messaging system they do it by using logical addresses +of the system, so called Message Channels. + +![alt text](./etc/message-channel.png "Message Channel") + +## Applicability +Use the Message Channel pattern when + +* two or more applications need to communicate using a messaging system + +## Real world examples + +* [akka-camel](http://doc.akka.io/docs/akka/snapshot/scala/camel.html) \ No newline at end of file diff --git a/message-channel/etc/message-channel.png b/message-channel/etc/message-channel.png new file mode 100644 index 000000000..7db473281 Binary files /dev/null and b/message-channel/etc/message-channel.png differ diff --git a/message-channel/etc/message-channel.ucls b/message-channel/etc/message-channel.ucls new file mode 100644 index 000000000..3ef0ed4bc --- /dev/null +++ b/message-channel/etc/message-channel.ucls @@ -0,0 +1,320 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/message-channel/pom.xml b/message-channel/pom.xml new file mode 100644 index 000000000..cbeba7a75 --- /dev/null +++ b/message-channel/pom.xml @@ -0,0 +1,51 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + message-channel + + + org.apache.camel + camel-core + + + org.apache.camel + camel-stream + + + junit + junit + test + + + diff --git a/message-channel/src/main/java/com/iluwatar/message/channel/App.java b/message-channel/src/main/java/com/iluwatar/message/channel/App.java new file mode 100644 index 000000000..dab04bd37 --- /dev/null +++ b/message-channel/src/main/java/com/iluwatar/message/channel/App.java @@ -0,0 +1,72 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.message.channel; + +import org.apache.camel.CamelContext; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.impl.DefaultCamelContext; + +/** + * + * When two applications communicate with each other using a messaging system they first need to + * establish a communication channel that will carry the data. Message Channel decouples Message + * producers and consumers. + *

+ * The sending application doesn't necessarily know what particular application will end up + * retrieving it, but it can be assured that the application that retrieves the information is + * interested in that information. This is because the messaging system has different Message + * Channels for different types of information the applications want to communicate. When an + * application sends information, it doesn't randomly add the information to any channel available; + * it adds it to a channel whose specific purpose is to communicate that sort of information. + * Likewise, an application that wants to receive particular information doesn't pull info off some + * random channel; it selects what channel to get information from based on what type of information + * it wants. + *

+ * In this example we use Apache Camel to establish two different Message Channels. The first one + * reads from standard input and delivers messages to Direct endpoint. The second Message Channel is + * established from the Direct component to console output. No actual messages are sent, only the + * established routes are printed to standard output. + * + */ +public class App { + + /** + * Program entry point + */ + public static void main(String[] args) throws Exception { + CamelContext context = new DefaultCamelContext(); + + context.addRoutes(new RouteBuilder() { + + @Override + public void configure() throws Exception { + from("stream:in").to("direct:greetings"); + from("direct:greetings").to("stream:out"); + } + }); + + context.start(); + context.getRoutes().stream().forEach(r -> System.out.println(r)); + context.stop(); + } +} diff --git a/message-channel/src/test/java/com/iluwatar/message/channel/AppTest.java b/message-channel/src/test/java/com/iluwatar/message/channel/AppTest.java new file mode 100644 index 000000000..ace457c16 --- /dev/null +++ b/message-channel/src/test/java/com/iluwatar/message/channel/AppTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.message.channel; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() throws Exception { + String[] args = {}; + App.main(args); + } +} diff --git a/model-view-controller/README.md b/model-view-controller/README.md new file mode 100644 index 000000000..bc96f7ab1 --- /dev/null +++ b/model-view-controller/README.md @@ -0,0 +1,29 @@ +--- +layout: pattern +title: Model-View-Controller +folder: model-view-controller +permalink: /patterns/model-view-controller/ +categories: Presentation Tier +tags: + - Java + - Difficulty-Intermediate +--- + +## Intent +Separate the user interface into three interconnected components: +the model, the view and the controller. Let the model manage the data, the view +display the data and the controller mediate updating the data and redrawing the +display. + +![alt text](./etc/model-view-controller.png "Model-View-Controller") + +## Applicability +Use the Model-View-Controller pattern when + +* you want to clearly separate the domain data from its user interface representation + +## Credits + +* [Trygve Reenskaug - Model-view-controller](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) +* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) +* [Patterns of Enterprise Application Architecture](http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420) diff --git a/model-view-controller/pom.xml b/model-view-controller/pom.xml index 8b5e1a250..3c01f6e8d 100644 --- a/model-view-controller/pom.xml +++ b/model-view-controller/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT model-view-controller @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/App.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/App.java new file mode 100644 index 000000000..4a3d2b41e --- /dev/null +++ b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/App.java @@ -0,0 +1,63 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.model.view.controller; + +/** + * + * Model-View-Controller is a pattern for implementing user interfaces. It divides the application + * into three interconnected parts namely the model, the view and the controller. + *

+ * The central component of MVC, the model, captures the behavior of the application in terms of its + * problem domain, independent of the user interface. The model directly manages the data, logic and + * rules of the application. A view can be any output representation of information, such as a chart + * or a diagram The third part, the controller, accepts input and converts it to commands for the + * model or view. + *

+ * In this example we have a giant ({@link GiantModel}) with statuses for health, fatigue and + * nourishment. {@link GiantView} can display the giant with its current status. + * {@link GiantController} receives input affecting the model and delegates redrawing the giant to + * the view. + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + // create model, view and controller + GiantModel giant = new GiantModel(Health.HEALTHY, Fatigue.ALERT, Nourishment.SATURATED); + GiantView view = new GiantView(); + GiantController controller = new GiantController(giant, view); + // initial display + controller.updateView(); + // controller receives some interactions that affect the giant + controller.setHealth(Health.WOUNDED); + controller.setNourishment(Nourishment.HUNGRY); + controller.setFatigue(Fatigue.TIRED); + // redisplay + controller.updateView(); + } +} diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Fatigue.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Fatigue.java new file mode 100644 index 000000000..38d3030e0 --- /dev/null +++ b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Fatigue.java @@ -0,0 +1,44 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.model.view.controller; + +/** + * + * Fatigue enumeration + * + */ +public enum Fatigue { + + ALERT("alert"), TIRED("tired"), SLEEPING("sleeping"); + + private String title; + + Fatigue(String title) { + this.title = title; + } + + @Override + public String toString() { + return title; + } +} diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantController.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantController.java new file mode 100644 index 000000000..e291e682c --- /dev/null +++ b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantController.java @@ -0,0 +1,67 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.model.view.controller; + +/** + * + * GiantController can update the giant data and redraw it using the view. + * + */ +public class GiantController { + + private GiantModel giant; + private GiantView view; + + public GiantController(GiantModel giant, GiantView view) { + this.giant = giant; + this.view = view; + } + + public Health getHealth() { + return giant.getHealth(); + } + + public void setHealth(Health health) { + this.giant.setHealth(health); + } + + public Fatigue getFatigue() { + return giant.getFatigue(); + } + + public void setFatigue(Fatigue fatigue) { + this.giant.setFatigue(fatigue); + } + + public Nourishment getNourishment() { + return giant.getNourishment(); + } + + public void setNourishment(Nourishment nourishment) { + this.giant.setNourishment(nourishment); + } + + public void updateView() { + this.view.displayGiant(giant); + } +} diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantModel.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantModel.java new file mode 100644 index 000000000..9b9303de7 --- /dev/null +++ b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantModel.java @@ -0,0 +1,70 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.model.view.controller; + +/** + * + * GiantModel contains the giant data + * + */ +public class GiantModel { + + private Health health; + private Fatigue fatigue; + private Nourishment nourishment; + + GiantModel(Health health, Fatigue fatigue, Nourishment nourishment) { + this.health = health; + this.fatigue = fatigue; + this.nourishment = nourishment; + } + + public Health getHealth() { + return health; + } + + public void setHealth(Health health) { + this.health = health; + } + + public Fatigue getFatigue() { + return fatigue; + } + + public void setFatigue(Fatigue fatigue) { + this.fatigue = fatigue; + } + + public Nourishment getNourishment() { + return nourishment; + } + + public void setNourishment(Nourishment nourishment) { + this.nourishment = nourishment; + } + + @Override + public String toString() { + return String.format("The giant looks %s, %s and %s.", health, fatigue, nourishment); + } +} diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantView.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantView.java new file mode 100644 index 000000000..dd4361487 --- /dev/null +++ b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantView.java @@ -0,0 +1,35 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.model.view.controller; + +/** + * + * GiantView displays the giant + * + */ +public class GiantView { + + public void displayGiant(GiantModel giant) { + System.out.println(giant); + } +} diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Health.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Health.java new file mode 100644 index 000000000..4efe0100b --- /dev/null +++ b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Health.java @@ -0,0 +1,44 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.model.view.controller; + +/** + * + * Health enumeration + * + */ +public enum Health { + + HEALTHY("healthy"), WOUNDED("wounded"), DEAD("dead"); + + private String title; + + Health(String title) { + this.title = title; + } + + @Override + public String toString() { + return title; + } +} diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Nourishment.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Nourishment.java new file mode 100644 index 000000000..e2d41ccf8 --- /dev/null +++ b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Nourishment.java @@ -0,0 +1,44 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.model.view.controller; + +/** + * + * Nourishment enumeration + * + */ +public enum Nourishment { + + SATURATED("saturated"), HUNGRY("hungry"), STARVING("starving"); + + private String title; + + Nourishment(String title) { + this.title = title; + } + + @Override + public String toString() { + return title; + } +} diff --git a/model-view-controller/src/main/java/com/iluwatar/modelviewcontroller/App.java b/model-view-controller/src/main/java/com/iluwatar/modelviewcontroller/App.java deleted file mode 100644 index eb9fbdc8f..000000000 --- a/model-view-controller/src/main/java/com/iluwatar/modelviewcontroller/App.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.iluwatar.modelviewcontroller; - -/** - * - * Model-View-Controller is a pattern for implementing user interfaces. It divides the application - * into three interconnected parts namely the model, the view and the controller. - * - * The central component of MVC, the model, captures the behavior of the application in terms of its problem - * domain, independent of the user interface. The model directly manages the data, logic and rules of the - * application. A view can be any output representation of information, such as a chart or a diagram - * The third part, the controller, accepts input and converts it to commands for the model or view. - * - * In this example we have a giant (GiantModel) with statuses for health, fatigue and nourishment. GiantView - * can display the giant with its current status. GiantController receives input affecting the model and - * delegates redrawing the giant to the view. - * - */ -public class App { - - public static void main( String[] args ) { - // create model, view and controller - GiantModel giant = new GiantModel(Health.HEALTHY, Fatigue.ALERT, Nourishment.SATURATED); - GiantView view = new GiantView(); - GiantController controller = new GiantController(giant, view); - // initial display - controller.updateView(); - // controller receives some interactions that affect the giant - controller.setHealth(Health.WOUNDED); - controller.setNourishment(Nourishment.HUNGRY); - controller.setFatigue(Fatigue.TIRED); - // redisplay - controller.updateView(); - } -} diff --git a/model-view-controller/src/main/java/com/iluwatar/modelviewcontroller/Fatigue.java b/model-view-controller/src/main/java/com/iluwatar/modelviewcontroller/Fatigue.java deleted file mode 100644 index 7faa5f71b..000000000 --- a/model-view-controller/src/main/java/com/iluwatar/modelviewcontroller/Fatigue.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.iluwatar.modelviewcontroller; - -/** - * - * Fatigue enumeration - * - */ -public enum Fatigue { - - ALERT("alert"), TIRED("tired"), SLEEPING("sleeping"); - - private String title; - - Fatigue(String title) { - this.title = title; - } - - @Override - public String toString() { - return title; - } -} diff --git a/model-view-controller/src/main/java/com/iluwatar/modelviewcontroller/GiantController.java b/model-view-controller/src/main/java/com/iluwatar/modelviewcontroller/GiantController.java deleted file mode 100644 index ab5f6d127..000000000 --- a/model-view-controller/src/main/java/com/iluwatar/modelviewcontroller/GiantController.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.iluwatar.modelviewcontroller; - -/** - * - * GiantController can update the giant data and redraw it using the view. - * - */ -public class GiantController { - - private GiantModel giant; - private GiantView view; - - public GiantController(GiantModel giant, GiantView view) { - this.giant = giant; - this.view = view; - } - - public Health getHealth() { - return giant.getHealth(); - } - - public void setHealth(Health health) { - this.giant.setHealth(health); - } - - public Fatigue getFatigue() { - return giant.getFatigue(); - } - - public void setFatigue(Fatigue fatigue) { - this.giant.setFatigue(fatigue); - } - - public Nourishment getNourishment() { - return giant.getNourishment(); - } - - public void setNourishment(Nourishment nourishment) { - this.giant.setNourishment(nourishment); - } - - public void updateView() { - this.view.displayGiant(giant); - } -} diff --git a/model-view-controller/src/main/java/com/iluwatar/modelviewcontroller/GiantModel.java b/model-view-controller/src/main/java/com/iluwatar/modelviewcontroller/GiantModel.java deleted file mode 100644 index f623cd0a1..000000000 --- a/model-view-controller/src/main/java/com/iluwatar/modelviewcontroller/GiantModel.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.iluwatar.modelviewcontroller; - -/** - * - * GiantModel contains the giant data - * - */ -public class GiantModel { - - private Health health; - private Fatigue fatigue; - private Nourishment nourishment; - - GiantModel(Health health, Fatigue fatigue, Nourishment nourishment) { - this.health = health; - this.fatigue = fatigue; - this.nourishment = nourishment; - } - - public Health getHealth() { - return health; - } - - public void setHealth(Health health) { - this.health = health; - } - - public Fatigue getFatigue() { - return fatigue; - } - - public void setFatigue(Fatigue fatigue) { - this.fatigue = fatigue; - } - - public Nourishment getNourishment() { - return nourishment; - } - - public void setNourishment(Nourishment nourishment) { - this.nourishment = nourishment; - } - - @Override - public String toString() { - return String.format("The giant looks %s, %s and %s.", health, fatigue, nourishment); - } -} diff --git a/model-view-controller/src/main/java/com/iluwatar/modelviewcontroller/GiantView.java b/model-view-controller/src/main/java/com/iluwatar/modelviewcontroller/GiantView.java deleted file mode 100644 index df23119c6..000000000 --- a/model-view-controller/src/main/java/com/iluwatar/modelviewcontroller/GiantView.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.iluwatar.modelviewcontroller; - -/** - * - * GiantView displays the giant - * - */ -public class GiantView { - - public void displayGiant(GiantModel giant) { - System.out.println(giant); - } -} diff --git a/model-view-controller/src/main/java/com/iluwatar/modelviewcontroller/Health.java b/model-view-controller/src/main/java/com/iluwatar/modelviewcontroller/Health.java deleted file mode 100644 index 263182c20..000000000 --- a/model-view-controller/src/main/java/com/iluwatar/modelviewcontroller/Health.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.iluwatar.modelviewcontroller; - -/** - * - * Health enumeration - * - */ -public enum Health { - - HEALTHY("healthy"), WOUNDED("wounded"), DEAD("dead"); - - private String title; - - Health(String title) { - this.title = title; - } - - @Override - public String toString() { - return title; - } -} diff --git a/model-view-controller/src/main/java/com/iluwatar/modelviewcontroller/Nourishment.java b/model-view-controller/src/main/java/com/iluwatar/modelviewcontroller/Nourishment.java deleted file mode 100644 index 799983e2a..000000000 --- a/model-view-controller/src/main/java/com/iluwatar/modelviewcontroller/Nourishment.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.iluwatar.modelviewcontroller; - -/** - * - * Nourishment enumeration - * - */ -public enum Nourishment { - - SATURATED("saturated"), HUNGRY("hungry"), STARVING("starving"); - - private String title; - - Nourishment(String title) { - this.title = title; - } - - @Override - public String toString() { - return title; - } -} diff --git a/model-view-controller/src/test/java/com/iluwatar/model/view/controller/AppTest.java b/model-view-controller/src/test/java/com/iluwatar/model/view/controller/AppTest.java new file mode 100644 index 000000000..aad850d69 --- /dev/null +++ b/model-view-controller/src/test/java/com/iluwatar/model/view/controller/AppTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.model.view.controller; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/model-view-controller/src/test/java/com/iluwatar/model/view/controller/GiantControllerTest.java b/model-view-controller/src/test/java/com/iluwatar/model/view/controller/GiantControllerTest.java new file mode 100644 index 000000000..7e0532d30 --- /dev/null +++ b/model-view-controller/src/test/java/com/iluwatar/model/view/controller/GiantControllerTest.java @@ -0,0 +1,122 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.model.view.controller; + +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/20/15 - 2:19 PM + * + * @author Jeroen Meulemeester + */ +public class GiantControllerTest { + + /** + * Verify if the controller passes the health level through to the model and vice versa + */ + @Test + public void testSetHealth() { + final GiantModel model = mock(GiantModel.class); + final GiantView view = mock(GiantView.class); + final GiantController controller = new GiantController(model, view); + + verifyZeroInteractions(model, view); + + for (final Health health : Health.values()) { + controller.setHealth(health); + verify(model).setHealth(health); + verifyZeroInteractions(view); + } + + controller.getHealth(); + verify(model).getHealth(); + + verifyNoMoreInteractions(model, view); + } + + /** + * Verify if the controller passes the fatigue level through to the model and vice versa + */ + @Test + public void testSetFatigue() { + final GiantModel model = mock(GiantModel.class); + final GiantView view = mock(GiantView.class); + final GiantController controller = new GiantController(model, view); + + verifyZeroInteractions(model, view); + + for (final Fatigue fatigue : Fatigue.values()) { + controller.setFatigue(fatigue); + verify(model).setFatigue(fatigue); + verifyZeroInteractions(view); + } + + controller.getFatigue(); + verify(model).getFatigue(); + + verifyNoMoreInteractions(model, view); + } + + /** + * Verify if the controller passes the nourishment level through to the model and vice versa + */ + @Test + public void testSetNourishment() { + final GiantModel model = mock(GiantModel.class); + final GiantView view = mock(GiantView.class); + final GiantController controller = new GiantController(model, view); + + verifyZeroInteractions(model, view); + + for (final Nourishment nourishment : Nourishment.values()) { + controller.setNourishment(nourishment); + verify(model).setNourishment(nourishment); + verifyZeroInteractions(view); + } + + controller.getNourishment(); + verify(model).getNourishment(); + + verifyNoMoreInteractions(model, view); + } + + @Test + public void testUpdateView() { + final GiantModel model = mock(GiantModel.class); + final GiantView view = mock(GiantView.class); + final GiantController controller = new GiantController(model, view); + + verifyZeroInteractions(model, view); + + controller.updateView(); + verify(view).displayGiant(model); + + verifyNoMoreInteractions(model, view); + } + +} \ No newline at end of file diff --git a/model-view-controller/src/test/java/com/iluwatar/model/view/controller/GiantModelTest.java b/model-view-controller/src/test/java/com/iluwatar/model/view/controller/GiantModelTest.java new file mode 100644 index 000000000..8e322fc59 --- /dev/null +++ b/model-view-controller/src/test/java/com/iluwatar/model/view/controller/GiantModelTest.java @@ -0,0 +1,78 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.model.view.controller; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Date: 12/20/15 - 2:10 PM + * + * @author Jeroen Meulemeester + */ +public class GiantModelTest { + + /** + * Verify if the health value is set properly though the constructor and setter + */ + @Test + public void testSetHealth() { + final GiantModel model = new GiantModel(Health.HEALTHY, Fatigue.ALERT, Nourishment.SATURATED); + assertEquals(Health.HEALTHY, model.getHealth()); + for (final Health health : Health.values()) { + model.setHealth(health); + assertEquals(health, model.getHealth()); + assertEquals("The giant looks " + health.toString() + ", alert and saturated.", model.toString()); + } + } + + /** + * Verify if the fatigue level is set properly though the constructor and setter + */ + @Test + public void testSetFatigue() { + final GiantModel model = new GiantModel(Health.HEALTHY, Fatigue.ALERT, Nourishment.SATURATED); + assertEquals(Fatigue.ALERT, model.getFatigue()); + for (final Fatigue fatigue : Fatigue.values()) { + model.setFatigue(fatigue); + assertEquals(fatigue, model.getFatigue()); + assertEquals("The giant looks healthy, " + fatigue.toString() + " and saturated.", model.toString()); + } + } + + /** + * Verify if the nourishment level is set properly though the constructor and setter + */ + @Test + public void testSetNourishment() { + final GiantModel model = new GiantModel(Health.HEALTHY, Fatigue.ALERT, Nourishment.SATURATED); + assertEquals(Nourishment.SATURATED, model.getNourishment()); + for (final Nourishment nourishment : Nourishment.values()) { + model.setNourishment(nourishment); + assertEquals(nourishment, model.getNourishment()); + assertEquals("The giant looks healthy, alert and " + nourishment.toString() + ".", model.toString()); + } + } + +} diff --git a/model-view-controller/src/test/java/com/iluwatar/model/view/controller/GiantViewTest.java b/model-view-controller/src/test/java/com/iluwatar/model/view/controller/GiantViewTest.java new file mode 100644 index 000000000..89d503d4e --- /dev/null +++ b/model-view-controller/src/test/java/com/iluwatar/model/view/controller/GiantViewTest.java @@ -0,0 +1,86 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.model.view.controller; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/20/15 - 2:04 PM + * + * @author Jeroen Meulemeester + */ +public class GiantViewTest { + + /** + * The mocked standard out {@link PrintStream}, required since the actions of the views don't have + * any influence on any other accessible objects, except for writing to std-out using {@link + * System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Verify if the {@link GiantView} does what it has to do: Print the {@link GiantModel} to the + * standard out stream, nothing more, nothing less. + */ + @Test + public void testDisplayGiant() { + final GiantView view = new GiantView(); + + final GiantModel model = mock(GiantModel.class); + view.displayGiant(model); + + verify(this.stdOutMock).println(model); + verifyNoMoreInteractions(model, this.stdOutMock); + + } + +} \ No newline at end of file diff --git a/model-view-controller/src/test/java/com/iluwatar/modelviewcontroller/AppTest.java b/model-view-controller/src/test/java/com/iluwatar/modelviewcontroller/AppTest.java deleted file mode 100644 index f9c8eca8e..000000000 --- a/model-view-controller/src/test/java/com/iluwatar/modelviewcontroller/AppTest.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.iluwatar.modelviewcontroller; - -import org.junit.Test; - -import com.iluwatar.modelviewcontroller.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} diff --git a/model-view-presenter/README.md b/model-view-presenter/README.md new file mode 100644 index 000000000..a3b921ce4 --- /dev/null +++ b/model-view-presenter/README.md @@ -0,0 +1,27 @@ +--- +layout: pattern +title: Model-View-Presenter +folder: model-view-presenter +permalink: /patterns/model-view-presenter/ +categories: Presentation Tier +tags: + - Java + - Difficulty-Intermediate +--- + +## Intent +Apply a "Separation of Concerns" principle in a way that allows +developers to build and test user interfaces. + +![alt text](./etc/model-view-presenter_1.png "Model-View-Presenter") + +## Applicability +Use the Model-View-Presenter in any of the following +situations + +* when you want to improve the "Separation of Concerns" principle in presentation logic +* when a user interface development and testing is necessary. + +## Real world examples + +* [MVP4J](https://github.com/amineoualialami/mvp4j) diff --git a/model-view-presenter/etc/data/test.txt b/model-view-presenter/etc/data/test.txt index 997ae361a..85aa49612 100644 --- a/model-view-presenter/etc/data/test.txt +++ b/model-view-presenter/etc/data/test.txt @@ -1,2 +1,25 @@ +==== + The MIT License + Copyright (c) 2014 Ilkka Seppälä + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +==== + Test line 1 Test line 2 \ No newline at end of file diff --git a/model-view-presenter/etc/model-view-presenter.ucls b/model-view-presenter/etc/model-view-presenter.ucls index aa8dd13ae..70e905d3f 100644 --- a/model-view-presenter/etc/model-view-presenter.ucls +++ b/model-view-presenter/etc/model-view-presenter.ucls @@ -1,8 +1,9 @@ - - - + file="/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorPresenter.java" + binary="false" corner="BOTTOM_RIGHT"> @@ -32,9 +33,9 @@ - - - - - - - - + - - + + + + + + - - - - - - - - - - - - - - - - - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/model-view-presenter/pom.xml b/model-view-presenter/pom.xml index a00deaac3..0aad02b27 100644 --- a/model-view-presenter/pom.xml +++ b/model-view-presenter/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT model-view-presenter model-view-presenter diff --git a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/App.java b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/App.java new file mode 100644 index 000000000..2c8985252 --- /dev/null +++ b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/App.java @@ -0,0 +1,54 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.model.view.presenter; + +/** + * + * The Model-View-Presenter(MVP) architectural pattern, helps us achieve what is called + * "The separation of concerns" principle. This is accomplished by separating the application's + * logic (Model), GUIs (View), and finally the way that the user's actions update the application's + * logic (Presenter). + *

+ * In the following example, The {@link FileLoader} class represents the app's logic, the + * {@link FileSelectorJFrame} is the GUI and the {@link FileSelectorPresenter} is responsible to + * respond to users' actions. + *

+ * Finally, please notice the wiring between the Presenter and the View and between the Presenter + * and the Model. + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + FileLoader loader = new FileLoader(); + FileSelectorJFrame jFrame = new FileSelectorJFrame(); + FileSelectorPresenter presenter = new FileSelectorPresenter(jFrame); + presenter.setLoader(loader); + presenter.start(); + } +} diff --git a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileLoader.java b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileLoader.java new file mode 100644 index 000000000..b9e36fd00 --- /dev/null +++ b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileLoader.java @@ -0,0 +1,100 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.model.view.presenter; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; + +/** + * Every instance of this class represents the Model component in the Model-View-Presenter + * architectural pattern. + *

+ * It is responsible for reading and loading the contents of a given file. + */ +public class FileLoader { + + /** + * Indicates if the file is loaded or not. + */ + private boolean loaded; + + /** + * The name of the file that we want to load. + */ + private String fileName; + + /** + * Loads the data of the file specified. + */ + public String loadData() { + try { + BufferedReader br = new BufferedReader(new FileReader(new File(this.fileName))); + StringBuilder sb = new StringBuilder(); + String line; + + while ((line = br.readLine()) != null) { + sb.append(line).append('\n'); + } + + this.loaded = true; + br.close(); + + return sb.toString(); + } catch (Exception e) { + e.printStackTrace(); + } + + return null; + } + + /** + * Sets the path of the file to be loaded, to the given value. + * + * @param fileName The path of the file to be loaded. + */ + public void setFileName(String fileName) { + this.fileName = fileName; + } + + /** + * @return fileName The path of the file to be loaded. + */ + public String getFileName() { + return this.fileName; + } + + /** + * @return True, if the file given exists, false otherwise. + */ + public boolean fileExists() { + return new File(this.fileName).exists(); + } + + /** + * @return True, if the file is loaded, false otherwise. + */ + public boolean isLoaded() { + return this.loaded; + } +} diff --git a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorJFrame.java b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorJFrame.java new file mode 100644 index 000000000..9cf883086 --- /dev/null +++ b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorJFrame.java @@ -0,0 +1,218 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.model.view.presenter; + +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JTextField; + +/** + * This class is the GUI implementation of the View component in the Model-View-Presenter pattern. + */ +public class FileSelectorJFrame extends JFrame implements FileSelectorView, ActionListener { + + /** + * Default serial version ID. + */ + private static final long serialVersionUID = 1L; + + /** + * The "OK" button for loading the file. + */ + private JButton ok; + + /** + * The cancel button. + */ + private JButton cancel; + + /** + * The information label. + */ + private JLabel info; + + /** + * The contents label. + */ + private JLabel contents; + + /** + * The text field for giving the name of the file that we want to open. + */ + private JTextField input; + + /** + * A text area that will keep the contents of the file opened. + */ + private JTextArea area; + + /** + * The panel that will hold our widgets. + */ + private JPanel panel; + + /** + * The Presenter component that the frame will interact with + */ + private FileSelectorPresenter presenter; + + /** + * The name of the file that we want to read it's contents. + */ + private String fileName; + + /** + * Constructor. + */ + public FileSelectorJFrame() { + super("File Loader"); + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + this.setLayout(null); + this.setBounds(100, 100, 500, 200); + + /* + * Add the panel. + */ + this.panel = new JPanel(); + panel.setLayout(null); + this.add(panel); + panel.setBounds(0, 0, 500, 200); + panel.setBackground(Color.LIGHT_GRAY); + + /* + * Add the info label. + */ + this.info = new JLabel("File Name :"); + this.panel.add(info); + info.setBounds(30, 10, 100, 30); + + /* + * Add the contents label. + */ + this.contents = new JLabel("File contents :"); + this.panel.add(contents); + this.contents.setBounds(30, 100, 120, 30); + + /* + * Add the text field. + */ + this.input = new JTextField(100); + this.panel.add(input); + this.input.setBounds(150, 15, 200, 20); + + /* + * Add the text area. + */ + this.area = new JTextArea(100, 100); + JScrollPane pane = new JScrollPane(area); + pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + this.panel.add(pane); + this.area.setEditable(false); + pane.setBounds(150, 100, 250, 80); + + /* + * Add the OK button. + */ + this.ok = new JButton("OK"); + this.panel.add(ok); + this.ok.setBounds(250, 50, 100, 25); + this.ok.addActionListener(this); + + /* + * Add the cancel button. + */ + this.cancel = new JButton("Cancel"); + this.panel.add(this.cancel); + this.cancel.setBounds(380, 50, 100, 25); + this.cancel.addActionListener(this); + + this.presenter = null; + this.fileName = null; + } + + @Override + public void actionPerformed(ActionEvent e) { + if (this.ok.equals(e.getSource())) { + this.fileName = this.input.getText(); + presenter.fileNameChanged(); + presenter.confirmed(); + } else if (this.cancel.equals(e.getSource())) { + presenter.cancelled(); + } + } + + @Override + public void open() { + this.setVisible(true); + } + + @Override + public void close() { + this.dispose(); + } + + @Override + public boolean isOpened() { + return this.isVisible(); + } + + @Override + public void setPresenter(FileSelectorPresenter presenter) { + this.presenter = presenter; + } + + @Override + public FileSelectorPresenter getPresenter() { + return this.presenter; + } + + @Override + public void setFileName(String name) { + this.fileName = name; + } + + @Override + public String getFileName() { + return this.fileName; + } + + @Override + public void showMessage(String message) { + JOptionPane.showMessageDialog(null, message); + } + + @Override + public void displayData(String data) { + this.area.setText(data); + } +} diff --git a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorPresenter.java b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorPresenter.java new file mode 100644 index 000000000..54f9a91c6 --- /dev/null +++ b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorPresenter.java @@ -0,0 +1,99 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.model.view.presenter; + +/** + * Every instance of this class represents the Presenter component in the Model-View-Presenter + * architectural pattern. + *

+ * It is responsible for reacting to the user's actions and update the View component. + */ +public class FileSelectorPresenter { + + /** + * The View component that the presenter interacts with. + */ + private FileSelectorView view; + + /** + * The Model component that the presenter interacts with. + */ + private FileLoader loader; + + /** + * Constructor + * + * @param view The view component that the presenter will interact with. + */ + public FileSelectorPresenter(FileSelectorView view) { + this.view = view; + } + + /** + * Sets the {@link FileLoader} object, to the value given as parameter. + * + * @param loader The new {@link FileLoader} object(the Model component). + */ + public void setLoader(FileLoader loader) { + this.loader = loader; + } + + /** + * Starts the presenter. + */ + public void start() { + view.setPresenter(this); + view.open(); + } + + /** + * An "event" that fires when the name of the file to be loaded changes. + */ + public void fileNameChanged() { + loader.setFileName(view.getFileName()); + } + + /** + * Ok button handler + */ + public void confirmed() { + if (loader.getFileName() == null || loader.getFileName().equals("")) { + view.showMessage("Please give the name of the file first!"); + return; + } + + if (loader.fileExists()) { + String data = loader.loadData(); + view.displayData(data); + } else { + view.showMessage("The file specified does not exist."); + } + } + + /** + * Cancels the file loading process. + */ + public void cancelled() { + view.close(); + } +} diff --git a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorStub.java b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorStub.java new file mode 100644 index 000000000..b9ce73878 --- /dev/null +++ b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorStub.java @@ -0,0 +1,131 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.model.view.presenter; + +/** + * Every instance of this class represents the Stub component in the Model-View-Presenter + * architectural pattern. + *

+ * The stub implements the View interface and it is useful when we want the test the reaction to + * user events, such as mouse clicks. + *

+ * Since we can not test the GUI directly, the MVP pattern provides this functionality through the + * View's dummy implementation, the Stub. + */ +public class FileSelectorStub implements FileSelectorView { + + /** + * Indicates whether or not the view is opened. + */ + private boolean opened; + + /** + * The presenter Component. + */ + private FileSelectorPresenter presenter; + + /** + * The current name of the file. + */ + private String name; + + /** + * Indicates the number of messages that were "displayed" to the user. + */ + private int numOfMessageSent; + + /** + * Indicates if the data of the file where displayed or not. + */ + private boolean dataDisplayed; + + /** + * Constructor + */ + public FileSelectorStub() { + this.opened = false; + this.presenter = null; + this.name = ""; + this.numOfMessageSent = 0; + this.dataDisplayed = false; + } + + @Override + public void open() { + this.opened = true; + } + + @Override + public void setPresenter(FileSelectorPresenter presenter) { + this.presenter = presenter; + } + + @Override + public boolean isOpened() { + return this.opened; + } + + @Override + public FileSelectorPresenter getPresenter() { + return this.presenter; + } + + @Override + public String getFileName() { + return this.name; + } + + @Override + public void setFileName(String name) { + this.name = name; + } + + @Override + public void showMessage(String message) { + this.numOfMessageSent++; + } + + @Override + public void close() { + this.opened = false; + } + + @Override + public void displayData(String data) { + this.dataDisplayed = true; + } + + /** + * Returns the number of messages that were displayed to the user. + */ + public int getMessagesSent() { + return this.numOfMessageSent; + } + + /** + * @return True if the data where displayed, false otherwise. + */ + public boolean dataDisplayed() { + return this.dataDisplayed; + } +} diff --git a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorView.java b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorView.java new file mode 100644 index 000000000..94edcf7d0 --- /dev/null +++ b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorView.java @@ -0,0 +1,83 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.model.view.presenter; + +/** + * This interface represents the View component in the Model-View-Presenter pattern. It can be + * implemented by either the GUI components, or by the Stub. + */ +public interface FileSelectorView { + + /** + * Opens the view. + */ + void open(); + + /** + * Closes the view. + */ + void close(); + + /** + * @return True, if the view is opened, false otherwise. + */ + boolean isOpened(); + + /** + * Sets the presenter component, to the one given as parameter. + * + * @param presenter The new presenter component. + */ + void setPresenter(FileSelectorPresenter presenter); + + /** + * @return The presenter Component. + */ + FileSelectorPresenter getPresenter(); + + /** + * Sets the file's name, to the value given as parameter. + * + * @param name The new name of the file. + */ + void setFileName(String name); + + /** + * @return The name of the file. + */ + String getFileName(); + + /** + * Displays a message to the users. + * + * @param message The message to be displayed. + */ + void showMessage(String message); + + /** + * Displays the data to the view. + * + * @param data The data to be written. + */ + void displayData(String data); +} diff --git a/model-view-presenter/src/main/java/com/iluwatar/modelviewpresenter/FileLoader.java b/model-view-presenter/src/main/java/com/iluwatar/modelviewpresenter/FileLoader.java deleted file mode 100644 index a0b0abdc9..000000000 --- a/model-view-presenter/src/main/java/com/iluwatar/modelviewpresenter/FileLoader.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.iluwatar.modelviewpresenter; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; - -/** - * Every instance of this class represents the Model component in the - * Model-View-Presenter architectural pattern. - * - * It is responsible for reading and loading the contents of a given file. - */ -public class FileLoader { - - /** - * Indicates if the file is loaded or not. - */ - private boolean loaded = false; - - /** - * The name of the file that we want to load. - */ - private String fileName; - - /** - * Loads the data of the file specified. - */ - public String loadData() { - try { - BufferedReader br = new BufferedReader(new FileReader(new File( - this.fileName))); - StringBuilder sb = new StringBuilder(); - String line; - - while ((line = br.readLine()) != null) { - sb.append(line).append('\n'); - } - - this.loaded = true; - br.close(); - - return sb.toString(); - } - - catch (Exception e) { - e.printStackTrace(); - } - - return null; - } - - /** - * Sets the path of the file to be loaded, to the given value. - * - * @param fileName - * The path of the file to be loaded. - */ - public void setFileName(String fileName) { - this.fileName = fileName; - } - - /** - * @return fileName The path of the file to be loaded. - */ - public String getFileName() { - return this.fileName; - } - - /** - * @return True, if the file given exists, false otherwise. - */ - public boolean fileExists() { - return new File(this.fileName).exists(); - } - - /** - * @return True, if the file is loaded, false otherwise. - */ - public boolean isLoaded() { - return this.loaded; - } -} diff --git a/model-view-presenter/src/main/java/com/iluwatar/modelviewpresenter/FileSelectorJFrame.java b/model-view-presenter/src/main/java/com/iluwatar/modelviewpresenter/FileSelectorJFrame.java deleted file mode 100644 index 025d17dab..000000000 --- a/model-view-presenter/src/main/java/com/iluwatar/modelviewpresenter/FileSelectorJFrame.java +++ /dev/null @@ -1,200 +0,0 @@ -package com.iluwatar.modelviewpresenter; - -import java.awt.Color; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTextArea; -import javax.swing.JTextField; - -/** - * This class is the GUI implementation of the View component In the - * Model-View-Presenter pattern. - */ -public class FileSelectorJFrame extends JFrame implements FileSelectorView, - ActionListener { - - /** - * Default serial version ID. - */ - private static final long serialVersionUID = 1L; - - /** - * The "OK" button for loading the file. - */ - private JButton OK; - - /** - * The cancel button. - */ - private JButton cancel; - - /** - * The information label. - */ - private JLabel info; - - /** - * The contents label. - */ - private JLabel contents; - - /** - * The text field for giving the name of the file that we want to open. - */ - private JTextField input; - - /** - * A text area that will keep the contents of the file opened. - */ - private JTextArea area; - - /** - * The panel that will hold our widgets. - */ - private JPanel panel; - - /** - * The Presenter component that the frame will interact with - */ - private FileSelectorPresenter presenter; - - /** - * The name of the file that we want to read it's contents. - */ - private String fileName; - - /** - * Constructor. - */ - public FileSelectorJFrame() { - super("File Loader"); - this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - this.setLayout(null); - this.setBounds(100, 100, 500, 200); - - /* - * Add the panel. - */ - this.panel = new JPanel(); - panel.setLayout(null); - this.add(panel); - panel.setBounds(0, 0, 500, 200); - panel.setBackground(Color.LIGHT_GRAY); - - /* - * Add the info label. - */ - this.info = new JLabel("File Name :"); - this.panel.add(info); - info.setBounds(30, 10, 100, 30); - - /* - * Add the contents label. - */ - this.contents = new JLabel("File contents :"); - this.panel.add(contents); - this.contents.setBounds(30, 100, 120, 30); - - /* - * Add the text field. - */ - this.input = new JTextField(100); - this.panel.add(input); - this.input.setBounds(150, 15, 200, 20); - - /* - * Add the text area. - */ - this.area = new JTextArea(100, 100); - JScrollPane pane = new JScrollPane(area); - pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); - this.panel.add(pane); - this.area.setEditable(false); - pane.setBounds(150, 100, 250, 80); - - /* - * Add the OK button. - */ - this.OK = new JButton("OK"); - this.panel.add(OK); - this.OK.setBounds(250, 50, 100, 25); - this.OK.addActionListener(this); - - /* - * Add the cancel button. - */ - this.cancel = new JButton("Cancel"); - this.panel.add(this.cancel); - this.cancel.setBounds(380, 50, 100, 25); - this.cancel.addActionListener(this); - - this.presenter = null; - this.fileName = null; - } - - @Override - public void actionPerformed(ActionEvent e) { - if (e.getSource() == this.OK) { - this.fileName = this.input.getText(); - presenter.fileNameChanged(); - presenter.confirmed(); - } - - else if (e.getSource() == this.cancel) { - presenter.cancelled(); - } - } - - @Override - public void open() { - this.setVisible(true); - } - - @Override - public void close() { - this.dispose(); - } - - @Override - public boolean isOpened() { - return this.isVisible(); - } - - @Override - public void setPresenter(FileSelectorPresenter presenter) { - this.presenter = presenter; - } - - @Override - public FileSelectorPresenter getPresenter() { - return this.presenter; - } - - @Override - public void setFileName(String name) { - this.fileName = name; - } - - @Override - public String getFileName() { - return this.fileName; - } - - @Override - public void showMessage(String message) { - JOptionPane.showMessageDialog(null, message); - } - - @Override - public void displayData(String data) { - this.area.setText(data); - } -} diff --git a/model-view-presenter/src/main/java/com/iluwatar/modelviewpresenter/FileSelectorPresenter.java b/model-view-presenter/src/main/java/com/iluwatar/modelviewpresenter/FileSelectorPresenter.java deleted file mode 100644 index 80aac42ff..000000000 --- a/model-view-presenter/src/main/java/com/iluwatar/modelviewpresenter/FileSelectorPresenter.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.iluwatar.modelviewpresenter; - -/** - * Every instance of this class represents the Presenter component in the - * Model-View-Presenter architectural pattern. - * - * It is responsible for reacting to the user's actions and update the View - * component. - */ -public class FileSelectorPresenter { - - /** - * The View component that the presenter interacts with. - */ - private FileSelectorView view; - - /** - * The Model component that the presenter interacts with. - */ - private FileLoader loader; - - /** - * Constructor - * - * @param view - * The view component that the presenter will interact with. - */ - public FileSelectorPresenter(FileSelectorView view) { - this.view = view; - } - - /** - * Sets the FileLoader object, to the value given as parameter. - * - * @param loader - * The new FileLoader object(the Model component). - */ - public void setLoader(FileLoader loader) { - this.loader = loader; - } - - /** - * Starts the presenter. - */ - public void start() { - view.setPresenter(this); - view.open(); - } - - /** - * An "event" that fires when the name of the file to be loaded changes. - */ - public void fileNameChanged() { - loader.setFileName(view.getFileName()); - } - - public void confirmed() { - if (loader.getFileName() == null || loader.getFileName().equals("")) { - view.showMessage("Please give the name of the file first!"); - return; - } - - if (loader.fileExists()) { - String data = loader.loadData(); - view.displayData(data); - } - - else { - view.showMessage("The file specified does not exist."); - } - } - - /** - * Cancels the file loading process. - */ - public void cancelled() { - view.close(); - } -} diff --git a/model-view-presenter/src/main/java/com/iluwatar/modelviewpresenter/FileSelectorStub.java b/model-view-presenter/src/main/java/com/iluwatar/modelviewpresenter/FileSelectorStub.java deleted file mode 100644 index 8b7ff7dd6..000000000 --- a/model-view-presenter/src/main/java/com/iluwatar/modelviewpresenter/FileSelectorStub.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.iluwatar.modelviewpresenter; - -/** - * Every instance of this class represents the Stub component in the - * Model-View-Presenter architectural pattern. - * - * The stub implements the View interface and it is useful when we want the test - * the reaction to user events, such as mouse clicks. - * - * Since we can not test the GUI directly, the MVP pattern provides this - * functionality through the View's dummy implementation, the Stub. - */ -public class FileSelectorStub implements FileSelectorView { - - /** - * Indicates whether or not the view is opened. - */ - private boolean opened; - - /** - * The presenter Component. - */ - private FileSelectorPresenter presenter; - - /** - * The current name of the file. - */ - private String name; - - /** - * Indicates the number of messages that were "displayed" to the user. - */ - private int numOfMessageSent; - - /** - * Indicates if the data of the file where displayed or not. - */ - private boolean dataDisplayed; - - /** - * Constructor - */ - public FileSelectorStub() { - this.opened = false; - this.presenter = null; - this.name = ""; - this.numOfMessageSent = 0; - this.dataDisplayed = false; - } - - @Override - public void open() { - this.opened = true; - } - - @Override - public void setPresenter(FileSelectorPresenter presenter) { - this.presenter = presenter; - } - - @Override - public boolean isOpened() { - return this.opened; - } - - @Override - public FileSelectorPresenter getPresenter() { - return this.presenter; - } - - @Override - public String getFileName() { - return this.name; - } - - @Override - public void setFileName(String name) { - this.name = name; - } - - @Override - public void showMessage(String message) { - this.numOfMessageSent++; - } - - @Override - public void close() { - this.opened = false; - } - - @Override - public void displayData(String data) { - this.dataDisplayed = true; - } - - /** - * Returns the number of messages that were displayed to the user. - */ - public int getMessagesSent() { - return this.numOfMessageSent; - } - - /** - * @return True if the data where displayed, false otherwise. - */ - public boolean dataDisplayed() { - return this.dataDisplayed; - } -} diff --git a/model-view-presenter/src/main/java/com/iluwatar/modelviewpresenter/FileSelectorView.java b/model-view-presenter/src/main/java/com/iluwatar/modelviewpresenter/FileSelectorView.java deleted file mode 100644 index 424a0eed3..000000000 --- a/model-view-presenter/src/main/java/com/iluwatar/modelviewpresenter/FileSelectorView.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.iluwatar.modelviewpresenter; - -/** - * This interface represents the View component in the Model-View-Presenter - * pattern. It can be implemented by either the GUI components, or by the Stub. - */ -public interface FileSelectorView { - - /** - * Opens the view. - */ - public void open(); - - /** - * Closes the view. - */ - public void close(); - - /** - * @return True, if the view is opened, false otherwise. - */ - public boolean isOpened(); - - /** - * Sets the presenter component, to the one given as parameter. - * - * @param presenter - * The new presenter component. - */ - public void setPresenter(FileSelectorPresenter presenter); - - /** - * @return The presenter Component. - */ - public FileSelectorPresenter getPresenter(); - - /** - * Sets the file's name, to the value given as parameter. - * - * @param name - * The new name of the file. - */ - public void setFileName(String name); - - /** - * @return The name of the file. - */ - public String getFileName(); - - /** - * Displays a message to the users. - * - * @param message - * The message to be displayed. - */ - public void showMessage(String message); - - /** - * Displays the data to the view. - * - * @param data - * The data to be written. - */ - public void displayData(String data); -} diff --git a/model-view-presenter/src/main/java/com/iluwatar/modelviewpresenter/MainApp.java b/model-view-presenter/src/main/java/com/iluwatar/modelviewpresenter/MainApp.java deleted file mode 100644 index 96e2fdab2..000000000 --- a/model-view-presenter/src/main/java/com/iluwatar/modelviewpresenter/MainApp.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.iluwatar.modelviewpresenter; - -/** - * - * The Model-View-Presenter(MVP) architectural pattern, helps us achieve what is - * called "The separation of concerns" principle. This is accomplished - * by separating the application's logic(Model), GUIs(View), and finally - * the way that the user's actions update the application's logic(Presenter). - * - * In the following example, The FileLoader class represents the app's logic, - * the FileSelectorJFrame is the GUI and the FileSelectorPresenter is - * responsible to respond to users' actions. - * - * Finally, please notice the wiring between the Presenter and the View - * and between the Presenter and the Model. - * - */ -public class MainApp { - - public static void main(String[] args) { - FileLoader loader = new FileLoader(); - FileSelectorJFrame jFrame = new FileSelectorJFrame(); - FileSelectorPresenter presenter = new FileSelectorPresenter(jFrame); - presenter.setLoader(loader); - presenter.start(); - } -} diff --git a/model-view-presenter/src/test/java/com/iluwatar/model/view/presenter/AppTest.java b/model-view-presenter/src/test/java/com/iluwatar/model/view/presenter/AppTest.java new file mode 100644 index 000000000..4e329e3bc --- /dev/null +++ b/model-view-presenter/src/test/java/com/iluwatar/model/view/presenter/AppTest.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.model.view.presenter; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } + +} diff --git a/model-view-presenter/src/test/java/com/iluwatar/model/view/presenter/FileLoaderTest.java b/model-view-presenter/src/test/java/com/iluwatar/model/view/presenter/FileLoaderTest.java new file mode 100644 index 000000000..eed01b835 --- /dev/null +++ b/model-view-presenter/src/test/java/com/iluwatar/model/view/presenter/FileLoaderTest.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.model.view.presenter; + +import org.junit.Test; + +import static org.junit.Assert.assertNull; + +/** + * Date: 12/21/15 - 12:12 PM + * + * @author Jeroen Meulemeester + */ +public class FileLoaderTest { + + @Test + public void testLoadData() throws Exception { + final FileLoader fileLoader = new FileLoader(); + fileLoader.setFileName("non-existing-file"); + assertNull(fileLoader.loadData()); + } + +} \ No newline at end of file diff --git a/model-view-presenter/src/test/java/com/iluwatar/model/view/presenter/FileSelectorPresenterTest.java b/model-view-presenter/src/test/java/com/iluwatar/model/view/presenter/FileSelectorPresenterTest.java new file mode 100644 index 000000000..42afdb2b8 --- /dev/null +++ b/model-view-presenter/src/test/java/com/iluwatar/model/view/presenter/FileSelectorPresenterTest.java @@ -0,0 +1,145 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.model.view.presenter; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Before; +import org.junit.Test; + +/** + * This test case is responsible for testing our application by taking advantage of the + * Model-View-Controller architectural pattern. + */ +public class FileSelectorPresenterTest { + + /** + * The Presenter component. + */ + private FileSelectorPresenter presenter; + + /** + * The View component, implemented this time as a Stub!!! + */ + private FileSelectorStub stub; + + /** + * The Model component. + */ + private FileLoader loader; + + /** + * Initializes the components of the test case. + */ + @Before + public void setUp() { + this.stub = new FileSelectorStub(); + this.loader = new FileLoader(); + presenter = new FileSelectorPresenter(this.stub); + presenter.setLoader(loader); + } + + /** + * Tests if the Presenter was successfully connected with the View. + */ + @Test + public void wiring() { + presenter.start(); + + assertNotNull(stub.getPresenter()); + assertTrue(stub.isOpened()); + } + + /** + * Tests if the name of the file changes. + */ + @Test + public void updateFileNameToLoader() { + String expectedFile = "Stamatis"; + stub.setFileName(expectedFile); + + presenter.start(); + presenter.fileNameChanged(); + + assertEquals(expectedFile, loader.getFileName()); + } + + /** + * Tests if we receive a confirmation when we attempt to open a file that it's name is null or an + * empty string. + */ + @Test + public void fileConfirmationWhenNameIsNull() { + stub.setFileName(null); + + presenter.start(); + presenter.fileNameChanged(); + presenter.confirmed(); + + assertFalse(loader.isLoaded()); + assertEquals(1, stub.getMessagesSent()); + } + + /** + * Tests if we receive a confirmation when we attempt to open a file that it doesn't exist. + */ + @Test + public void fileConfirmationWhenFileDoesNotExist() { + stub.setFileName("RandomName.txt"); + + presenter.start(); + presenter.fileNameChanged(); + presenter.confirmed(); + + assertFalse(loader.isLoaded()); + assertEquals(1, stub.getMessagesSent()); + } + + /** + * Tests if we can open the file, when it exists. + */ + @Test + public void fileConfirmationWhenFileExists() { + stub.setFileName("etc/data/test.txt"); + presenter.start(); + presenter.fileNameChanged(); + presenter.confirmed(); + + assertTrue(loader.isLoaded()); + assertTrue(stub.dataDisplayed()); + } + + /** + * Tests if the view closes after cancellation. + */ + @Test + public void cancellation() { + presenter.start(); + presenter.cancelled(); + + assertFalse(stub.isOpened()); + } +} diff --git a/model-view-presenter/src/test/java/com/iluwatar/modelviewpresenter/FileSelectorPresenterTest.java b/model-view-presenter/src/test/java/com/iluwatar/modelviewpresenter/FileSelectorPresenterTest.java deleted file mode 100644 index 9895b99d5..000000000 --- a/model-view-presenter/src/test/java/com/iluwatar/modelviewpresenter/FileSelectorPresenterTest.java +++ /dev/null @@ -1,125 +0,0 @@ -package com.iluwatar.modelviewpresenter; - -import static org.junit.Assert.*; - -import org.junit.Before; -import org.junit.Test; - -import com.iluwatar.modelviewpresenter.FileLoader; -import com.iluwatar.modelviewpresenter.FileSelectorPresenter; -import com.iluwatar.modelviewpresenter.FileSelectorStub; - -/** - * This test case is responsible for testing our application by taking advantage - * of the Model-View-Controller architectural pattern. - */ -public class FileSelectorPresenterTest { - - /** - * The Presenter component. - */ - private FileSelectorPresenter presenter; - - /** - * The View component, implemented this time as a Stub!!! - */ - private FileSelectorStub stub; - - /** - * The Model component. - */ - private FileLoader loader; - - /** - * Initializes the components of the test case. - */ - @Before - public void setUp() { - this.stub = new FileSelectorStub(); - this.loader = new FileLoader(); - presenter = new FileSelectorPresenter(this.stub); - presenter.setLoader(loader); - } - - /** - * Tests if the Presenter was successfully connected with the View. - */ - @Test - public void wiring() { - presenter.start(); - - assertNotNull(stub.getPresenter()); - assertTrue(stub.isOpened()); - } - - /** - * Tests if the name of the file changes. - */ - @Test - public void updateFileNameToLoader() { - String EXPECTED_FILE = "Stamatis"; - stub.setFileName(EXPECTED_FILE); - - presenter.start(); - presenter.fileNameChanged(); - - assertEquals(EXPECTED_FILE, loader.getFileName()); - } - - /** - * Tests if we receive a confirmation when we attempt to open a file that - * it's name is null or an empty string. - */ - @Test - public void fileConfirmationWhenNameIsNull() { - stub.setFileName(null); - - presenter.start(); - presenter.fileNameChanged(); - presenter.confirmed(); - - assertFalse(loader.isLoaded()); - assertEquals(1, stub.getMessagesSent()); - } - - /** - * Tests if we receive a confirmation when we attempt to open a file that it - * doesn't exist. - */ - @Test - public void fileConfirmationWhenFileDoesNotExist() { - stub.setFileName("RandomName.txt"); - - presenter.start(); - presenter.fileNameChanged(); - presenter.confirmed(); - - assertFalse(loader.isLoaded()); - assertEquals(1, stub.getMessagesSent()); - } - - /** - * Tests if we can open the file, when it exists. - */ - @Test - public void fileConfirmationWhenFileExists() { - stub.setFileName("etc/data/test.txt"); - presenter.start(); - presenter.fileNameChanged(); - presenter.confirmed(); - - assertTrue(loader.isLoaded()); - assertTrue(stub.dataDisplayed()); - } - - /** - * Tests if the view closes after cancellation. - */ - @Test - public void cancellation() { - presenter.start(); - presenter.cancelled(); - - assertFalse(stub.isOpened()); - } -} diff --git a/monad/README.md b/monad/README.md new file mode 100644 index 000000000..41edd3d92 --- /dev/null +++ b/monad/README.md @@ -0,0 +1,35 @@ +--- +layout: pattern +title: Monad +folder: monad +permalink: /patterns/monad/ +categories: Other +tags: + - Java + - Difficulty-Expert + - Functional +--- + +## Intent + +Monad pattern based on monad from linear algebra represents the way of chaining operations +together step by step. Binding functions can be described as passing one's output to another's input +basing on the 'same type' contract. Formally, monad consists of a type constructor M and two +operations: +bind - that takes monadic object and a function from plain object to monadic value and returns monadic value +return - that takes plain type object and returns this object wrapped in a monadic value. + +![alt text](./etc/monad.png "Monad") + +## Applicability + +Use the Monad in any of the following situations + +* when you want to chain operations easily +* when you want to apply each function regardless of the result of any of them + +## Credits + +* [Design Pattern Reloaded by Remi Forax](https://youtu.be/-k2X7guaArU) +* [Brian Beckman: Don't fear the Monad](https://channel9.msdn.com/Shows/Going+Deep/Brian-Beckman-Dont-fear-the-Monads) +* [Monad on Wikipedia](https://en.wikipedia.org/wiki/Monad_(functional_programming)) \ No newline at end of file diff --git a/monad/etc/monad.png b/monad/etc/monad.png new file mode 100644 index 000000000..f82e7a3e4 Binary files /dev/null and b/monad/etc/monad.png differ diff --git a/monad/etc/monad.ucls b/monad/etc/monad.ucls new file mode 100644 index 000000000..9c98df7c6 --- /dev/null +++ b/monad/etc/monad.ucls @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/monad/pom.xml b/monad/pom.xml new file mode 100644 index 000000000..7f128272d --- /dev/null +++ b/monad/pom.xml @@ -0,0 +1,43 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + monad + + + junit + junit + test + + + + diff --git a/monad/src/main/java/com/iluwatar/monad/App.java b/monad/src/main/java/com/iluwatar/monad/App.java new file mode 100644 index 000000000..7b28fdcf8 --- /dev/null +++ b/monad/src/main/java/com/iluwatar/monad/App.java @@ -0,0 +1,57 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.monad; + +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * The Monad pattern defines a monad structure, that enables chaining operations + * in pipelines and processing data step by step. + * Formally, monad consists of a type constructor M and two operations: + *
bind - that takes monadic object and a function from plain object to the + * monadic value and returns monadic value. + *
return - that takes plain type object and returns this object wrapped in a monadic value. + *

+ * In the given example, the Monad pattern is represented as a {@link Validator} that takes an instance + * of a plain object with {@link Validator#of(Object)} + * and validates it {@link Validator#validate(Function, Predicate, String)} against given predicates. + *

As a validation result {@link Validator#get()} it either returns valid object {@link Validator#t} + * or throws a list of exceptions {@link Validator#exceptions} collected during validation. + */ +public class App { + + /** + * Program entry point. + * + * @param args @param args command line args + */ + public static void main(String[] args) { + User user = new User("user", 24, Sex.FEMALE, "foobar.com"); + System.out.println(Validator.of(user).validate(User::getName, Objects::nonNull, "name is null") + .validate(User::getName, name -> !name.isEmpty(), "name is empty") + .validate(User::getEmail, email -> !email.contains("@"), "email doesn't containt '@'") + .validate(User::getAge, age -> age > 20 && age < 30, "age isn't between...").get().toString()); + } +} diff --git a/monad/src/main/java/com/iluwatar/monad/Sex.java b/monad/src/main/java/com/iluwatar/monad/Sex.java new file mode 100644 index 000000000..b5d094d4b --- /dev/null +++ b/monad/src/main/java/com/iluwatar/monad/Sex.java @@ -0,0 +1,27 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.monad; + +public enum Sex { + MALE, FEMALE +} diff --git a/monad/src/main/java/com/iluwatar/monad/User.java b/monad/src/main/java/com/iluwatar/monad/User.java new file mode 100644 index 000000000..471094526 --- /dev/null +++ b/monad/src/main/java/com/iluwatar/monad/User.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.monad; + +public class User { + + private String name; + private int age; + private Sex sex; + private String email; + + /** + * @param name - name + * @param age - age + * @param sex - sex + * @param email - email address + */ + public User(String name, int age, Sex sex, String email) { + this.name = name; + this.age = age; + this.sex = sex; + this.email = email; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } + + public Sex getSex() { + return sex; + } + + public String getEmail() { + return email; + } +} diff --git a/monad/src/main/java/com/iluwatar/monad/Validator.java b/monad/src/main/java/com/iluwatar/monad/Validator.java new file mode 100644 index 000000000..cc4f36020 --- /dev/null +++ b/monad/src/main/java/com/iluwatar/monad/Validator.java @@ -0,0 +1,113 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.monad; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * Class representing Monad design pattern. Monad is a way of chaining operations on the + * given object together step by step. In Validator each step results in either success or + * failure indicator, giving a way of receiving each of them easily and finally getting + * validated object or list of exceptions. + * + * @param Placeholder for an object. + */ +public class Validator { + /** + * Object that is validated + */ + private final T t; + + /** + * List of exception thrown during validation. + */ + private final List exceptions = new ArrayList<>(); + + /** + * Creates a monadic value of given object. + * @param t object to be validated + */ + private Validator(T t) { + this.t = t; + } + + /** + * Creates validator against given object + * + * @param t object to be validated + * @param object's type + * @return new instance of a validator + */ + public static Validator of(T t) { + return new Validator<>(Objects.requireNonNull(t)); + } + + /** + * @param validation one argument boolean-valued function that + * represents one step of validation. Adds exception to main validation exception + * list when single step validation ends with failure. + * @param message error message when object is invalid + * @return this + */ + public Validator validate(Predicate validation, String message) { + if (!validation.test(t)) { + exceptions.add(new IllegalStateException(message)); + } + return this; + } + + /** + * Extension for the {@link Validator#validate(Function, Predicate, String)} method, + * dedicated for objects, that need to be projected before requested validation. + * + * @param projection function that gets an objects, and returns projection representing + * element to be validated. + * @param validation see {@link Validator#validate(Function, Predicate, String)} + * @param message see {@link Validator#validate(Function, Predicate, String)} + * @param see {@link Validator#validate(Function, Predicate, String)} + * @return this + */ + public Validator validate(Function projection, Predicate validation, + String message) { + return validate(projection.andThen(validation::test)::apply, message); + } + + /** + * Receives validated object or throws exception when invalid. + * + * @return object that was validated + * @throws IllegalStateException when any validation step results with failure + */ + public T get() throws IllegalStateException { + if (exceptions.isEmpty()) { + return t; + } + IllegalStateException e = new IllegalStateException(); + exceptions.forEach(e::addSuppressed); + throw e; + } +} diff --git a/monad/src/test/java/com/iluwatar/monad/AppTest.java b/monad/src/test/java/com/iluwatar/monad/AppTest.java new file mode 100644 index 000000000..78440b468 --- /dev/null +++ b/monad/src/test/java/com/iluwatar/monad/AppTest.java @@ -0,0 +1,35 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.monad; + +import org.junit.Test; + +public class AppTest { + + @Test + public void testMain() { + String[] args = {}; + App.main(args); + } + +} diff --git a/monad/src/test/java/com/iluwatar/monad/MonadTest.java b/monad/src/test/java/com/iluwatar/monad/MonadTest.java new file mode 100644 index 000000000..4ada7191d --- /dev/null +++ b/monad/src/test/java/com/iluwatar/monad/MonadTest.java @@ -0,0 +1,64 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.monad; + + +import junit.framework.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.util.Objects; + +public class MonadTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void testForInvalidName() { + thrown.expect(IllegalStateException.class); + User tom = new User(null, 21, Sex.MALE, "tom@foo.bar"); + Validator.of(tom).validate(User::getName, Objects::nonNull, "name cannot be null").get(); + } + + @Test + public void testForInvalidAge() { + thrown.expect(IllegalStateException.class); + User john = new User("John", 17, Sex.MALE, "john@qwe.bar"); + Validator.of(john).validate(User::getName, Objects::nonNull, "name cannot be null") + .validate(User::getAge, age -> age > 21, "user is underaged") + .get(); + } + + @Test + public void testForValid() { + User sarah = new User("Sarah", 42, Sex.FEMALE, "sarah@det.org"); + User validated = Validator.of(sarah).validate(User::getName, Objects::nonNull, "name cannot be null") + .validate(User::getAge, age -> age > 21, "user is underaged") + .validate(User::getSex, sex -> sex == Sex.FEMALE, "user is not female") + .validate(User::getEmail, email -> email.contains("@"), "email does not contain @ sign") + .get(); + Assert.assertSame(validated, sarah); + } +} diff --git a/monostate/README.md b/monostate/README.md new file mode 100644 index 000000000..3576dc659 --- /dev/null +++ b/monostate/README.md @@ -0,0 +1,35 @@ +--- +layout: pattern +title: MonoState +folder: monostate +permalink: /patterns/monostate/ +categories: Creational +tags: + - Java + - Difficulty-Beginner +--- + +## Also known as +Borg + +## Intent +Enforces a behaviour like sharing the same state amongst all instances. + +![alt text](./etc/monostate.png "MonoState") + +## Applicability +Use the Monostate pattern when + +* The same state must be shared across all instances of a class. +* Typically this pattern might be used everywhere a Singleton might be used. Singleton usage however is not transparent, Monostate usage is. +* Monostate has one major advantage over singleton. The subclasses might decorate the shared state as they wish and hence can provide dynamically different behaviour than the base class. + +## Typical Use Case + +* the logging class +* managing a connection to a database +* file manager + +## Real world examples + +Yet to see this. diff --git a/monostate/etc/MonoState.ucls b/monostate/etc/MonoState.ucls new file mode 100644 index 000000000..76b48ad0f --- /dev/null +++ b/monostate/etc/MonoState.ucls @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/monostate/etc/monostate.png b/monostate/etc/monostate.png new file mode 100644 index 000000000..273fa9545 Binary files /dev/null and b/monostate/etc/monostate.png differ diff --git a/monostate/pom.xml b/monostate/pom.xml new file mode 100644 index 000000000..64ee52abe --- /dev/null +++ b/monostate/pom.xml @@ -0,0 +1,47 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + monostate + + + junit + junit + test + + + org.mockito + mockito-core + test + + + diff --git a/monostate/src/main/java/com/iluwatar/monostate/App.java b/monostate/src/main/java/com/iluwatar/monostate/App.java new file mode 100644 index 000000000..bfb51fe91 --- /dev/null +++ b/monostate/src/main/java/com/iluwatar/monostate/App.java @@ -0,0 +1,57 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.monostate; + + + +/** + * + * The MonoState pattern ensures that all instances of the class will have the same state. This can + * be used a direct replacement of the Singleton pattern. + * + *

+ * In the following example, The {@link LoadBalancer} class represents the app's logic. It contains + * a series of Servers, which can handle requests of type {@link Request}. Two instances of + * LoadBalacer are created. When a request is made to a server via the first LoadBalancer the state + * change in the first load balancer affects the second. So if the first LoadBalancer selects the + * Server 1, the second LoadBalancer on a new request will select the Second server. If a third + * LoadBalancer is created and a new request is made to it, then it will select the third server as + * the second load balancer has already selected the second server. + *

+ * . + * + */ +public class App { + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + LoadBalancer loadBalancer1 = new LoadBalancer(); + LoadBalancer loadBalancer2 = new LoadBalancer(); + loadBalancer1.serverRequest(new Request("Hello")); + loadBalancer2.serverRequest(new Request("Hello World")); + } + +} diff --git a/monostate/src/main/java/com/iluwatar/monostate/LoadBalancer.java b/monostate/src/main/java/com/iluwatar/monostate/LoadBalancer.java new file mode 100644 index 000000000..613f0e105 --- /dev/null +++ b/monostate/src/main/java/com/iluwatar/monostate/LoadBalancer.java @@ -0,0 +1,80 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.monostate; + +import java.util.ArrayList; +import java.util.List; + +/** + * The LoadBalancer class. This implements the MonoState pattern. It holds a series of servers. Upon + * receiving a new Request, it delegates the call to the servers in a Round Robin Fashion. Since all + * instances of the class share the same state, all instances will delegate to the same server on + * receiving a new Request. + * + */ + +public class LoadBalancer { + private static List servers = new ArrayList<>(); + private static int id; + private static int lastServedId; + + static { + servers.add(new Server("localhost", 8081, ++id)); + servers.add(new Server("localhost", 8080, ++id)); + servers.add(new Server("localhost", 8082, ++id)); + servers.add(new Server("localhost", 8083, ++id)); + servers.add(new Server("localhost", 8084, ++id)); + } + + /** + * Add new server + */ + public final void addServer(Server server) { + synchronized (servers) { + servers.add(server); + } + + } + + public final int getNoOfServers() { + return servers.size(); + } + + public static int getLastServedId() { + return lastServedId; + } + + /** + * Handle request + */ + public void serverRequest(Request request) { + if (lastServedId >= servers.size()) { + lastServedId = 0; + } + Server server = servers.get(lastServedId++); + server.serve(request); + } + + + +} diff --git a/monostate/src/main/java/com/iluwatar/monostate/Request.java b/monostate/src/main/java/com/iluwatar/monostate/Request.java new file mode 100644 index 000000000..9cdb4d7e4 --- /dev/null +++ b/monostate/src/main/java/com/iluwatar/monostate/Request.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.monostate; + +/** + * + * The Request class. A {@link Server} can handle an instance of a Request. + * + */ + +public class Request { + public final String value; + + public Request(String value) { + this.value = value; + } +} diff --git a/monostate/src/main/java/com/iluwatar/monostate/Server.java b/monostate/src/main/java/com/iluwatar/monostate/Server.java new file mode 100644 index 000000000..bf700a57a --- /dev/null +++ b/monostate/src/main/java/com/iluwatar/monostate/Server.java @@ -0,0 +1,57 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.monostate; + +/** + * + * The Server class. Each Server sits behind a LoadBalancer which delegates the call to the servers + * in a simplistic Round Robin fashion. + * + */ +public class Server { + public final String host; + public final int port; + public final int id; + + /** + * Constructor + */ + public Server(String host, int port, int id) { + this.host = host; + this.port = port; + this.id = id; + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + public void serve(Request request) { + System.out.println("Server ID " + id + " associated to host : " + getHost() + " and Port " + + getPort() + " Processed request with value " + request.value); + } +} diff --git a/monostate/src/test/java/com/iluwatar/monostate/AppTest.java b/monostate/src/test/java/com/iluwatar/monostate/AppTest.java new file mode 100644 index 000000000..6b5f11a39 --- /dev/null +++ b/monostate/src/test/java/com/iluwatar/monostate/AppTest.java @@ -0,0 +1,35 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.monostate; + +import org.junit.Test; + +public class AppTest { + + @Test + public void testMain() { + String[] args = {}; + App.main(args); + } + +} diff --git a/monostate/src/test/java/com/iluwatar/monostate/LoadBalancerTest.java b/monostate/src/test/java/com/iluwatar/monostate/LoadBalancerTest.java new file mode 100644 index 000000000..70369e8a0 --- /dev/null +++ b/monostate/src/test/java/com/iluwatar/monostate/LoadBalancerTest.java @@ -0,0 +1,71 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.monostate; + +import org.junit.Assert; +import org.junit.Test; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; + +/** + * Date: 12/21/15 - 12:26 PM + * + * @author Jeroen Meulemeester + */ +public class LoadBalancerTest { + + @Test + public void testSameStateAmongstAllInstances() { + final LoadBalancer firstBalancer = new LoadBalancer(); + final LoadBalancer secondBalancer = new LoadBalancer(); + firstBalancer.addServer(new Server("localhost", 8085, 6)); + // Both should have the same number of servers. + Assert.assertTrue(firstBalancer.getNoOfServers() == secondBalancer.getNoOfServers()); + // Both Should have the same LastServedId + Assert.assertTrue(firstBalancer.getLastServedId() == secondBalancer.getLastServedId()); + } + + @Test + public void testServe() { + final Server server = mock(Server.class); + when(server.getHost()).thenReturn("testhost"); + when(server.getPort()).thenReturn(1234); + doNothing().when(server).serve(any(Request.class)); + + final LoadBalancer loadBalancer = new LoadBalancer(); + loadBalancer.addServer(server); + + verifyZeroInteractions(server); + + final Request request = new Request("test"); + for (int i = 0; i < loadBalancer.getNoOfServers() * 2; i++) { + loadBalancer.serverRequest(request); + } + + verify(server, times(2)).serve(request); + verifyNoMoreInteractions(server); + + } + +} diff --git a/multiton/README.md b/multiton/README.md new file mode 100644 index 000000000..0462ff0ec --- /dev/null +++ b/multiton/README.md @@ -0,0 +1,24 @@ +--- +layout: pattern +title: Multiton +folder: multiton +permalink: /patterns/multiton/ +categories: Creational +tags: + - Java + - Difficulty-Beginner +--- + +## Also known as +Registry + +## Intent +Ensure a class only has limited number of instances, and provide a +global point of access to them. + +![alt text](./etc/multiton.png "Multiton") + +## Applicability +Use the Multiton pattern when + +* there must be specific number of instances of a class, and they must be accessible to clients from a well-known access point diff --git a/multiton/pom.xml b/multiton/pom.xml index 52c71824b..0b835ed4d 100644 --- a/multiton/pom.xml +++ b/multiton/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT multiton diff --git a/multiton/src/main/java/com/iluwatar/multiton/App.java b/multiton/src/main/java/com/iluwatar/multiton/App.java index 7e3da2123..1ffd57a34 100644 --- a/multiton/src/main/java/com/iluwatar/multiton/App.java +++ b/multiton/src/main/java/com/iluwatar/multiton/App.java @@ -1,28 +1,54 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.multiton; /** * - * Whereas Singleton design pattern introduces single globally - * accessible object the Multiton pattern defines many globally - * accessible objects. The client asks for the correct instance - * from the Multiton by passing an enumeration as parameter. - * - * In this example Nazgul is the Multiton and we can ask single - * Nazgul from it using NazgulName. The Nazguls are statically - * initialized and stored in concurrent hash map. + * Whereas Singleton design pattern introduces single globally accessible object the Multiton + * pattern defines many globally accessible objects. The client asks for the correct instance from + * the Multiton by passing an enumeration as parameter. + *

+ * In this example {@link Nazgul} is the Multiton and we can ask single {@link Nazgul} from it using + * {@link NazgulName}. The {@link Nazgul}s are statically initialized and stored in concurrent hash + * map. * */ public class App { - - public static void main( String[] args ) { - System.out.println("KHAMUL=" + Nazgul.getInstance(NazgulName.KHAMUL)); - System.out.println("MURAZOR=" + Nazgul.getInstance(NazgulName.MURAZOR)); - System.out.println("DWAR=" + Nazgul.getInstance(NazgulName.DWAR)); - System.out.println("JI_INDUR=" + Nazgul.getInstance(NazgulName.JI_INDUR)); - System.out.println("AKHORAHIL=" + Nazgul.getInstance(NazgulName.AKHORAHIL)); - System.out.println("HOARMURATH=" + Nazgul.getInstance(NazgulName.HOARMURATH)); - System.out.println("ADUNAPHEL=" + Nazgul.getInstance(NazgulName.ADUNAPHEL)); - System.out.println("REN=" + Nazgul.getInstance(NazgulName.REN)); - System.out.println("UVATHA=" + Nazgul.getInstance(NazgulName.UVATHA)); - } + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + System.out.println("KHAMUL=" + Nazgul.getInstance(NazgulName.KHAMUL)); + System.out.println("MURAZOR=" + Nazgul.getInstance(NazgulName.MURAZOR)); + System.out.println("DWAR=" + Nazgul.getInstance(NazgulName.DWAR)); + System.out.println("JI_INDUR=" + Nazgul.getInstance(NazgulName.JI_INDUR)); + System.out.println("AKHORAHIL=" + Nazgul.getInstance(NazgulName.AKHORAHIL)); + System.out.println("HOARMURATH=" + Nazgul.getInstance(NazgulName.HOARMURATH)); + System.out.println("ADUNAPHEL=" + Nazgul.getInstance(NazgulName.ADUNAPHEL)); + System.out.println("REN=" + Nazgul.getInstance(NazgulName.REN)); + System.out.println("UVATHA=" + Nazgul.getInstance(NazgulName.UVATHA)); + } } diff --git a/multiton/src/main/java/com/iluwatar/multiton/Nazgul.java b/multiton/src/main/java/com/iluwatar/multiton/Nazgul.java index a099f9322..5a1dbede6 100644 --- a/multiton/src/main/java/com/iluwatar/multiton/Nazgul.java +++ b/multiton/src/main/java/com/iluwatar/multiton/Nazgul.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.multiton; import java.util.Map; @@ -5,38 +27,37 @@ import java.util.concurrent.ConcurrentHashMap; /** * - * Nazgul is a Multiton class. Nazgul instances can be queried - * using getInstance() method. + * Nazgul is a Multiton class. Nazgul instances can be queried using {@link #getInstance} method. * */ -public class Nazgul { +public final class Nazgul { - private static Map nazguls; - - private NazgulName name; + private static Map nazguls; - static { - nazguls = new ConcurrentHashMap<>(); - nazguls.put(NazgulName.KHAMUL, new Nazgul(NazgulName.KHAMUL)); - nazguls.put(NazgulName.MURAZOR, new Nazgul(NazgulName.MURAZOR)); - nazguls.put(NazgulName.DWAR, new Nazgul(NazgulName.DWAR)); - nazguls.put(NazgulName.JI_INDUR, new Nazgul(NazgulName.JI_INDUR)); - nazguls.put(NazgulName.AKHORAHIL, new Nazgul(NazgulName.AKHORAHIL)); - nazguls.put(NazgulName.HOARMURATH, new Nazgul(NazgulName.HOARMURATH)); - nazguls.put(NazgulName.ADUNAPHEL, new Nazgul(NazgulName.ADUNAPHEL)); - nazguls.put(NazgulName.REN, new Nazgul(NazgulName.REN)); - nazguls.put(NazgulName.UVATHA, new Nazgul(NazgulName.UVATHA)); - } - - private Nazgul(NazgulName name) { - this.name = name; - } + private NazgulName name; - public static Nazgul getInstance(NazgulName name) { - return nazguls.get(name); - } - - public NazgulName getName() { - return name; - } + static { + nazguls = new ConcurrentHashMap<>(); + nazguls.put(NazgulName.KHAMUL, new Nazgul(NazgulName.KHAMUL)); + nazguls.put(NazgulName.MURAZOR, new Nazgul(NazgulName.MURAZOR)); + nazguls.put(NazgulName.DWAR, new Nazgul(NazgulName.DWAR)); + nazguls.put(NazgulName.JI_INDUR, new Nazgul(NazgulName.JI_INDUR)); + nazguls.put(NazgulName.AKHORAHIL, new Nazgul(NazgulName.AKHORAHIL)); + nazguls.put(NazgulName.HOARMURATH, new Nazgul(NazgulName.HOARMURATH)); + nazguls.put(NazgulName.ADUNAPHEL, new Nazgul(NazgulName.ADUNAPHEL)); + nazguls.put(NazgulName.REN, new Nazgul(NazgulName.REN)); + nazguls.put(NazgulName.UVATHA, new Nazgul(NazgulName.UVATHA)); + } + + private Nazgul(NazgulName name) { + this.name = name; + } + + public static Nazgul getInstance(NazgulName name) { + return nazguls.get(name); + } + + public NazgulName getName() { + return name; + } } diff --git a/multiton/src/main/java/com/iluwatar/multiton/NazgulName.java b/multiton/src/main/java/com/iluwatar/multiton/NazgulName.java index f0db192a4..693dfc235 100644 --- a/multiton/src/main/java/com/iluwatar/multiton/NazgulName.java +++ b/multiton/src/main/java/com/iluwatar/multiton/NazgulName.java @@ -1,12 +1,34 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.multiton; /** * - * Each Nazgul has different NazgulName. + * Each Nazgul has different {@link NazgulName}. * */ public enum NazgulName { - KHAMUL, MURAZOR, DWAR, JI_INDUR, AKHORAHIL, HOARMURATH, ADUNAPHEL, REN, UVATHA; - + KHAMUL, MURAZOR, DWAR, JI_INDUR, AKHORAHIL, HOARMURATH, ADUNAPHEL, REN, UVATHA; + } diff --git a/multiton/src/test/java/com/iluwatar/multiton/AppTest.java b/multiton/src/test/java/com/iluwatar/multiton/AppTest.java index 9f5f96792..2d927b190 100644 --- a/multiton/src/test/java/com/iluwatar/multiton/AppTest.java +++ b/multiton/src/test/java/com/iluwatar/multiton/AppTest.java @@ -1,14 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.multiton; import org.junit.Test; -import com.iluwatar.multiton.App; - +/** + * + * Application test + * + */ public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } + + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/multiton/src/test/java/com/iluwatar/multiton/NazgulTest.java b/multiton/src/test/java/com/iluwatar/multiton/NazgulTest.java new file mode 100644 index 000000000..4aae5d87d --- /dev/null +++ b/multiton/src/test/java/com/iluwatar/multiton/NazgulTest.java @@ -0,0 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.multiton; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; + +/** + * Date: 12/22/15 - 22:28 AM + * + * @author Jeroen Meulemeester + */ +public class NazgulTest { + + /** + * Verify if {@link Nazgul#getInstance(NazgulName)} returns the correct Nazgul multiton instance + */ + @Test + public void testGetInstance() { + for (final NazgulName name : NazgulName.values()) { + final Nazgul nazgul = Nazgul.getInstance(name); + assertNotNull(nazgul); + assertSame(nazgul, Nazgul.getInstance(name)); + assertEquals(name, nazgul.getName()); + } + } + +} diff --git a/mute-idiom/README.md b/mute-idiom/README.md new file mode 100644 index 000000000..bb674b648 --- /dev/null +++ b/mute-idiom/README.md @@ -0,0 +1,28 @@ +--- +layout: pattern +title: Mute Idiom +folder: mute-idiom +permalink: /patterns/mute-idiom/ +categories: Other +tags: + - Java + - Difficulty-Beginner + - Idiom +--- + +## Intent +Provide a template to suppress any exceptions that either are declared but cannot occur or should only be logged; +while executing some business logic. The template removes the need to write repeated `try-catch` blocks. + + +![alt text](./etc/mute-idiom.png "Mute Idiom") + +## Applicability +Use this idiom when + +* an API declares some exception but can never throw that exception eg. ByteArrayOutputStream bulk write method. +* you need to suppress some exception just by logging it, such as closing a resource. + +## Credits + +* [JOOQ: Mute Design Pattern](http://blog.jooq.org/2016/02/18/the-mute-design-pattern/) diff --git a/mute-idiom/etc/mute-idiom.png b/mute-idiom/etc/mute-idiom.png new file mode 100644 index 000000000..203bdafc4 Binary files /dev/null and b/mute-idiom/etc/mute-idiom.png differ diff --git a/mute-idiom/etc/mute-idiom.ucls b/mute-idiom/etc/mute-idiom.ucls new file mode 100644 index 000000000..7b4d2f916 --- /dev/null +++ b/mute-idiom/etc/mute-idiom.ucls @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mute-idiom/pom.xml b/mute-idiom/pom.xml new file mode 100644 index 000000000..c138c5ed4 --- /dev/null +++ b/mute-idiom/pom.xml @@ -0,0 +1,34 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + mute-idiom + + + junit + junit + test + + + diff --git a/mute-idiom/src/main/java/com/iluwatar/mute/App.java b/mute-idiom/src/main/java/com/iluwatar/mute/App.java new file mode 100644 index 000000000..8a2aca41e --- /dev/null +++ b/mute-idiom/src/main/java/com/iluwatar/mute/App.java @@ -0,0 +1,103 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.mute; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.sql.SQLException; + +/** + * Mute pattern is utilized when we need to suppress an exception due to an API flaw or in + * situation when all we can do to handle the exception is to log it. + * This pattern should not be used everywhere. It is very important to logically handle the + * exceptions in a system, but some situations like the ones described above require this pattern, + * so that we don't need to repeat + *

+ * 
+ *   try {
+ *     // code that may throwing exception we need to ignore or may never be thrown
+ *   } catch (Exception ex) {
+ *     // ignore by logging or throw error if unexpected exception occurs
+ *   }
+ * 
+ * 
every time we need to ignore an exception. + * + */ +public class App { + + /** + * Program entry point. + * + * @param args command line args. + * @throws Exception if any exception occurs + */ + public static void main(String[] args) throws Exception { + + useOfLoggedMute(); + + useOfMute(); + } + + /* + * Typically used when the API declares some exception but cannot do so. Usually a + * signature mistake.In this example out is not supposed to throw exception as it is a + * ByteArrayOutputStream. So we utilize mute, which will throw AssertionError if unexpected + * exception occurs. + */ + private static void useOfMute() { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Mute.mute(() -> out.write("Hello".getBytes())); + } + + private static void useOfLoggedMute() throws SQLException { + Resource resource = null; + try { + resource = acquireResource(); + utilizeResource(resource); + } finally { + closeResource(resource); + } + } + + /* + * All we can do while failed close of a resource is to log it. + */ + private static void closeResource(Resource resource) { + Mute.loggedMute(() -> resource.close()); + } + + private static void utilizeResource(Resource resource) throws SQLException { + System.out.println("Utilizing acquired resource: " + resource); + } + + private static Resource acquireResource() throws SQLException { + return new Resource() { + + @Override + public void close() throws IOException { + throw new IOException("Error in closing resource: " + this); + } + }; + } +} diff --git a/mute-idiom/src/main/java/com/iluwatar/mute/CheckedRunnable.java b/mute-idiom/src/main/java/com/iluwatar/mute/CheckedRunnable.java new file mode 100644 index 000000000..d1440636f --- /dev/null +++ b/mute-idiom/src/main/java/com/iluwatar/mute/CheckedRunnable.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.mute; + +/** + * A runnable which may throw exception on execution. + * + */ +@FunctionalInterface +public interface CheckedRunnable { + /** + * Same as {@link Runnable#run()} with a possibility of exception in execution. + * @throws Exception if any exception occurs. + */ + void run() throws Exception; +} diff --git a/mute-idiom/src/main/java/com/iluwatar/mute/Mute.java b/mute-idiom/src/main/java/com/iluwatar/mute/Mute.java new file mode 100644 index 000000000..64169a8f5 --- /dev/null +++ b/mute-idiom/src/main/java/com/iluwatar/mute/Mute.java @@ -0,0 +1,68 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.mute; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * A utility class that allows you to utilize mute idiom. + */ +public final class Mute { + + // The constructor is never meant to be called. + private Mute() {} + + /** + * Executes the runnable and throws the exception occurred within a {@link AssertionError}. + * This method should be utilized to mute the operations that are guaranteed not to throw an exception. + * For instance {@link ByteArrayOutputStream#write(byte[])} declares in it's signature that it can throw + * an {@link IOException}, but in reality it cannot. This is because the bulk write method is not overridden + * in {@link ByteArrayOutputStream}. + * + * @param runnable a runnable that should never throw an exception on execution. + */ + public static void mute(CheckedRunnable runnable) { + try { + runnable.run(); + } catch (Exception e) { + throw new AssertionError(e); + } + } + + /** + * Executes the runnable and logs the exception occurred on {@link System#err}. + * This method should be utilized to mute the operations about which most you can do is log. + * For instance while closing a connection to database, or cleaning up a resource, + * all you can do is log the exception occurred. + * + * @param runnable a runnable that may throw an exception on execution. + */ + public static void loggedMute(CheckedRunnable runnable) { + try { + runnable.run(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/mute-idiom/src/main/java/com/iluwatar/mute/Resource.java b/mute-idiom/src/main/java/com/iluwatar/mute/Resource.java new file mode 100644 index 000000000..6970d06bc --- /dev/null +++ b/mute-idiom/src/main/java/com/iluwatar/mute/Resource.java @@ -0,0 +1,35 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.mute; + +import java.io.Closeable; + +/** + * Represents any resource that the application might acquire and that must be closed + * after it is utilized. Example of such resources can be a database connection, open + * files, sockets. + */ +public interface Resource extends Closeable { + +} diff --git a/mute-idiom/src/test/java/com/iluwatar/mute/AppTest.java b/mute-idiom/src/test/java/com/iluwatar/mute/AppTest.java new file mode 100644 index 000000000..8075d9c85 --- /dev/null +++ b/mute-idiom/src/test/java/com/iluwatar/mute/AppTest.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.mute; + +import org.junit.Test; + +/** + * Tests that Mute idiom example runs without errors. + * + */ +public class AppTest { + + @Test + public void test() throws Exception { + App.main(null); + } +} diff --git a/mute-idiom/src/test/java/com/iluwatar/mute/MuteTest.java b/mute-idiom/src/test/java/com/iluwatar/mute/MuteTest.java new file mode 100644 index 000000000..58cbfe893 --- /dev/null +++ b/mute-idiom/src/test/java/com/iluwatar/mute/MuteTest.java @@ -0,0 +1,78 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.mute; + +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class MuteTest { + + private static final String MESSAGE = "should not occur"; + + @Rule public ExpectedException exception = ExpectedException.none(); + + @Test + public void muteShouldRunTheCheckedRunnableAndNotThrowAnyExceptionIfCheckedRunnableDoesNotThrowAnyException() { + Mute.mute(() -> methodNotThrowingAnyException()); + } + + @Test + public void muteShouldRethrowUnexpectedExceptionAsAssertionError() throws Exception { + exception.expect(AssertionError.class); + exception.expectMessage(MESSAGE); + + Mute.mute(() -> methodThrowingException()); + } + + @Test + public void loggedMuteShouldRunTheCheckedRunnableAndNotThrowAnyExceptionIfCheckedRunnableDoesNotThrowAnyException() { + Mute.loggedMute(() -> methodNotThrowingAnyException()); + } + + @Test + public void loggedMuteShouldLogExceptionTraceBeforeSwallowingIt() throws IOException { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + System.setErr(new PrintStream(stream)); + + Mute.loggedMute(() -> methodThrowingException()); + + assertTrue(new String(stream.toByteArray()).contains(MESSAGE)); + } + + + private void methodNotThrowingAnyException() { + System.out.println("Executed successfully"); + } + + private void methodThrowingException() throws Exception { + throw new Exception(MESSAGE); + } +} diff --git a/mutex/README.md b/mutex/README.md new file mode 100644 index 000000000..84755872f --- /dev/null +++ b/mutex/README.md @@ -0,0 +1,30 @@ +--- +layout: pattern +title: Mutex +folder: mutex +permalink: /patterns/mutex/ +categories: Concurrency +tags: + - Java + - Difficulty-Intermediate +--- + +## Also known as +Mutual Exclusion Lock +Binary Semaphore + +## Intent +Create a lock which only allows a single thread to access a resource at any one instant. + +![alt text](./etc/mutex.png "Mutex") + +## Applicability +Use a Mutex when + +* you need to prevent two threads accessing a critical section at the same time +* concurrent access to a resource could lead to a race condition + +## Credits + +* [Lock (computer science)] (http://en.wikipedia.org/wiki/Lock_(computer_science)) +* [Semaphores] (http://tutorials.jenkov.com/java-concurrency/semaphores.html) diff --git a/mutex/etc/mutex.png b/mutex/etc/mutex.png new file mode 100644 index 000000000..3b7c966f8 Binary files /dev/null and b/mutex/etc/mutex.png differ diff --git a/mutex/pom.xml b/mutex/pom.xml new file mode 100644 index 000000000..852006cd4 --- /dev/null +++ b/mutex/pom.xml @@ -0,0 +1,42 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + mutex + + + junit + junit + test + + + diff --git a/mutex/src/main/java/com/iluwatar/mutex/App.java b/mutex/src/main/java/com/iluwatar/mutex/App.java new file mode 100644 index 000000000..f6494bd5c --- /dev/null +++ b/mutex/src/main/java/com/iluwatar/mutex/App.java @@ -0,0 +1,50 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.mutex; + +/** + * A Mutex prevents multiple threads from accessing a resource simultaneously. + *

+ * In this example we have two thieves who are taking beans from a jar. + * Only one thief can take a bean at a time. This is ensured by a Mutex lock + * which must be acquired in order to access the jar. Each thief attempts to + * acquire the lock, take a bean and then release the lock. If the lock has + * already been acquired, the thief will be prevented from continuing (blocked) + * until the lock has been released. The thieves stop taking beans once there + * are no beans left to take. + */ +public class App { + + /** + * main method + */ + public static void main(String[] args) { + Mutex mutex = new Mutex(); + Jar jar = new Jar(1000, mutex); + Thief peter = new Thief("Peter", jar); + Thief john = new Thief("John", jar); + peter.start(); + john.start(); + } + +} diff --git a/mutex/src/main/java/com/iluwatar/mutex/Jar.java b/mutex/src/main/java/com/iluwatar/mutex/Jar.java new file mode 100644 index 000000000..4fe0a4637 --- /dev/null +++ b/mutex/src/main/java/com/iluwatar/mutex/Jar.java @@ -0,0 +1,67 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.mutex; + +/** + * A Jar has a resource of beans which can only be accessed by a single Thief + * (thread) at any one time. A Mutex lock is used to prevent more than one Thief + * taking a bean simultaneously. + */ +public class Jar { + + /** + * The lock which must be acquired to access the beans resource. + */ + private final Lock lock; + + /** + * The resource within the jar. + */ + private int beans; + + public Jar(int beans, Lock lock) { + this.beans = beans; + this.lock = lock; + } + + /** + * Method for a thief to take a bean. + */ + public boolean takeBean() { + boolean success = false; + try { + lock.acquire(); + success = beans > 0; + if (success) { + beans = beans - 1; + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + lock.release(); + } + + return success; + } + +} diff --git a/mutex/src/main/java/com/iluwatar/mutex/Lock.java b/mutex/src/main/java/com/iluwatar/mutex/Lock.java new file mode 100644 index 000000000..fef67010e --- /dev/null +++ b/mutex/src/main/java/com/iluwatar/mutex/Lock.java @@ -0,0 +1,34 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.mutex; + +/** + * Lock is an interface for a lock which can be acquired and released. + */ +public interface Lock { + + void acquire() throws InterruptedException; + + void release(); + +} diff --git a/mutex/src/main/java/com/iluwatar/mutex/Mutex.java b/mutex/src/main/java/com/iluwatar/mutex/Mutex.java new file mode 100644 index 000000000..8e08534cd --- /dev/null +++ b/mutex/src/main/java/com/iluwatar/mutex/Mutex.java @@ -0,0 +1,67 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.mutex; + +/** + * Mutex is an implementation of a mutual exclusion lock. + */ +public class Mutex implements Lock { + + /** + * The current owner of the lock. + */ + private Object owner; + + /** + * Returns the current owner of the Mutex, or null if available + */ + public Object getOwner() { + return owner; + } + + /** + * Method called by a thread to acquire the lock. If the lock has already + * been acquired this will wait until the lock has been released to + * re-attempt the acquire. + */ + @Override + public synchronized void acquire() throws InterruptedException { + while (owner != null) { + wait(); + } + + owner = Thread.currentThread(); + } + + /** + * Method called by a thread to release the lock. + */ + @Override + public synchronized void release() { + if (Thread.currentThread() == owner) { + owner = null; + notify(); + } + } + +} diff --git a/mutex/src/main/java/com/iluwatar/mutex/Thief.java b/mutex/src/main/java/com/iluwatar/mutex/Thief.java new file mode 100644 index 000000000..d2225876c --- /dev/null +++ b/mutex/src/main/java/com/iluwatar/mutex/Thief.java @@ -0,0 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.mutex; + +/** + * Thief is a class which continually tries to acquire a jar and take a bean + * from it. When the jar is empty the thief stops. + */ +public class Thief extends Thread { + + /** + * The name of the thief. + */ + private final String name; + + /** + * The jar + */ + private final Jar jar; + + public Thief(String name, Jar jar) { + this.name = name; + this.jar = jar; + } + + /** + * In the run method the thief repeatedly tries to take a bean until none + * are left. + */ + @Override + public void run() { + int beans = 0; + + while (jar.takeBean()) { + beans = beans + 1; + System.out.println(name + " took a bean."); + } + + System.out.println(name + " took " + beans + " beans."); + } + +} diff --git a/mutex/src/test/java/com/iluwatar/mutex/AppTest.java b/mutex/src/test/java/com/iluwatar/mutex/AppTest.java new file mode 100644 index 000000000..f3a123519 --- /dev/null +++ b/mutex/src/test/java/com/iluwatar/mutex/AppTest.java @@ -0,0 +1,34 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.mutex; + +import org.junit.Test; +import java.io.IOException; + +public class AppTest{ + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } +} \ No newline at end of file diff --git a/mutex/src/test/java/com/iluwatar/mutex/JarTest.java b/mutex/src/test/java/com/iluwatar/mutex/JarTest.java new file mode 100644 index 000000000..97101466a --- /dev/null +++ b/mutex/src/test/java/com/iluwatar/mutex/JarTest.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.mutex; + +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * Test case for taking beans from a Jar + */ +public class JarTest { + + @Test + public void testTakeBeans() { + Mutex mutex = new Mutex(); + Jar jar = new Jar(10, mutex); + for (int i = 0; i < 10; i++) { + assertTrue(jar.takeBean()); + } + assertFalse(jar.takeBean()); + } + +} \ No newline at end of file diff --git a/mutex/src/test/java/com/iluwatar/mutex/MutexTest.java b/mutex/src/test/java/com/iluwatar/mutex/MutexTest.java new file mode 100644 index 000000000..8b3e82cb4 --- /dev/null +++ b/mutex/src/test/java/com/iluwatar/mutex/MutexTest.java @@ -0,0 +1,47 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.mutex; + +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * Test case for acquiring and releasing a Mutex + */ +public class MutexTest { + + @Test + public void acquireReleaseTest() { + Mutex mutex = new Mutex(); + assertNull(mutex.getOwner()); + try { + mutex.acquire(); + assertEquals(mutex.getOwner(), Thread.currentThread()); + } catch (InterruptedException e) { + fail(e.toString()); + } + mutex.release(); + assertNull(mutex.getOwner()); + } + +} \ No newline at end of file diff --git a/naked-objects/.gitattributes b/naked-objects/.gitattributes new file mode 100644 index 000000000..b1eafb692 --- /dev/null +++ b/naked-objects/.gitattributes @@ -0,0 +1,51 @@ +# +# +# text files are normalized (convert crlf => lf) +# binary files are not normalized (binary is a macro for -text -diff) +# +# + + +# Unless otherwise stated, assume text + +* text=auto + + +*.java text diff=java +*.html text diff=html +*.xhtml text diff=html +*.xml text +*.txt text + + +*.jar binary +*.so binary +*.dll binary + +# images +*.jpg binary +*.jpeg binary +*.png binary +*.pdn binary +*.pdn binary + + +*.cs text diff=csharp + +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain + diff --git a/naked-objects/.gitignore b/naked-objects/.gitignore new file mode 100644 index 000000000..0558e54af --- /dev/null +++ b/naked-objects/.gitignore @@ -0,0 +1,42 @@ +*~ +*.swp +*.class +bin/ +target/ +target-ide/ +logs/ +.settings/ +.project +.classpath +.idea +*.iml + +JArchitectOut/ +*.jdproj + +neo4j_DB/ + +# log files +datanucleus.log +isis.log +i18n-po.log +hs_err_pid*.log + +# Package Files # +*.jar +*.war +*.ear + +dependency-reduced-pom.xml +pom.xml.tag +pom.xml.next +pom.xml.releaseBackup +pom.xml.versionsBackup + +.clover/ +*.jdproj +JArchitectOut/ + + +rebel.xml +/translations.pot diff --git a/naked-objects/README b/naked-objects/README new file mode 100644 index 000000000..c3e693735 --- /dev/null +++ b/naked-objects/README @@ -0,0 +1 @@ +This is an Apache Isis application created with the SimpleApp archetype. The usage instructions can be found at http://isis.apache.org/guides/ug.html#_ug_getting-started_simpleapp-archetype \ No newline at end of file diff --git a/naked-objects/README.md b/naked-objects/README.md new file mode 100644 index 000000000..66e6ac2b0 --- /dev/null +++ b/naked-objects/README.md @@ -0,0 +1,32 @@ +--- +layout: pattern +title: Naked Objects +folder: naked-objects +permalink: /patterns/naked-objects/ +categories: Architectural +tags: + - Java + - Difficulty-Expert +--- + +## Intent +The Naked Objects architectural pattern is well suited for rapid +prototyping. Using the pattern, you only need to write the domain objects, +everything else is autogenerated by the framework. + +![alt text](./etc/naked-objects.png "Naked Objects") + +## Applicability +Use the Naked Objects pattern when + +* you are prototyping and need fast development cycle +* an autogenerated user interface is good enough +* you want to automatically publish the domain as REST services + +## Real world examples + +* [Apache Isis](https://isis.apache.org/) + +## Credits + +* [Richard Pawson - Naked Objects](https://isis.apache.org/resources/thesis/Pawson-Naked-Objects-thesis.pdf) diff --git a/naked-objects/dom/log4j.properties b/naked-objects/dom/log4j.properties new file mode 100644 index 000000000..ca165acc7 --- /dev/null +++ b/naked-objects/dom/log4j.properties @@ -0,0 +1,41 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# LOG4J Configuration +# =================== + +# Basic logging goes to "datanucleus.log" +log4j.appender.A1=org.apache.log4j.FileAppender +log4j.appender.A1.File=datanucleus.log +log4j.appender.A1.layout=org.apache.log4j.PatternLayout +log4j.appender.A1.layout.ConversionPattern=%d{HH:mm:ss,SSS} (%t) %-5p [%c] - %m%n +#log4j.appender.A1.Threshold=INFO + +# Categories +# Each category can be set to a "level", and to direct to an appender + +# Default to DEBUG level for all DataNucleus categories +log4j.logger.DataNucleus = DEBUG, A1 + +log4j.category.com.mchange.v2.c3p0=INFO, A1 +log4j.category.com.mchange.v2.resourcepool=INFO, A1 +log4j.category.org.logicalcobwebs.proxool=INFO,A1 + + +# Hbase libs logging +log4j.category.org.apache.hadoop=INFO,A1 +log4j.category.org.apache.zookeeper=INFO,A1 \ No newline at end of file diff --git a/naked-objects/dom/pom.xml b/naked-objects/dom/pom.xml new file mode 100644 index 000000000..119416694 --- /dev/null +++ b/naked-objects/dom/pom.xml @@ -0,0 +1,186 @@ + + + + 4.0.0 + + + com.iluwatar + naked-objects + 1.13.0-SNAPSHOT + + + naked-objects-dom + + + + + src/main/resources + + + src/main/java + + ** + + + **/*.java + + + + + + + + org.apache.isis.core + isis-core-applib + + + + org.apache.isis.core + isis-core-unittestsupport + test + + + + + org.objenesis + objenesis + test + + + + org.assertj + assertj-core + test + + + + + + + enhance + + true + + + 4.0.1 + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.datanucleus + datanucleus-maven-plugin + [${datanucleus-maven-plugin.version},) + + enhance + + + + + + + + + + + + + + + org.datanucleus + datanucleus-maven-plugin + ${datanucleus-maven-plugin.version} + + false + ${basedir}/log4j.properties + true + ${basedir}/datanucleus.properties + + + + process-classes + + enhance + + + + + + + + + org.datanucleus + datanucleus-core + + + org.datanucleus + datanucleus-jodatime + + + org.datanucleus + datanucleus-api-jdo + + + + + isis-validate + + + + + + org.apache.isis.tool + isis-maven-plugin + 1.9.0-SNAPSHOT + + ../webapp/src/main/webapp/WEB-INF + + + + org.apache.isis.example.application + simpleapp-dom + 1.9.0-SNAPSHOT + + + + com.google.guava + guava + 16.0.1 + + + + + test + + validate + + + + + + + + + + diff --git a/naked-objects/dom/src/main/java/META-INF/persistence.xml b/naked-objects/dom/src/main/java/META-INF/persistence.xml new file mode 100644 index 000000000..8824aa1ac --- /dev/null +++ b/naked-objects/dom/src/main/java/META-INF/persistence.xml @@ -0,0 +1,26 @@ + + + + + + + diff --git a/naked-objects/dom/src/main/java/domainapp/dom/app/homepage/HomePageService.java b/naked-objects/dom/src/main/java/domainapp/dom/app/homepage/HomePageService.java new file mode 100644 index 000000000..162cd3bb4 --- /dev/null +++ b/naked-objects/dom/src/main/java/domainapp/dom/app/homepage/HomePageService.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package domainapp.dom.app.homepage; + +import org.apache.isis.applib.DomainObjectContainer; +import org.apache.isis.applib.annotation.Action; +import org.apache.isis.applib.annotation.DomainService; +import org.apache.isis.applib.annotation.HomePage; +import org.apache.isis.applib.annotation.NatureOfService; +import org.apache.isis.applib.annotation.SemanticsOf; + +@DomainService(nature = NatureOfService.VIEW_CONTRIBUTIONS_ONLY) +public class HomePageService { + + // endregion + + // region > injected services + + @javax.inject.Inject + DomainObjectContainer container; + + // endregion + + // region > homePage (action) + + @Action(semantics = SemanticsOf.SAFE) + @HomePage + public HomePageViewModel homePage() { + return container.injectServicesInto(new HomePageViewModel()); + } + +} diff --git a/naked-objects/dom/src/main/java/domainapp/dom/app/homepage/HomePageViewModel.java b/naked-objects/dom/src/main/java/domainapp/dom/app/homepage/HomePageViewModel.java new file mode 100644 index 000000000..f367a39fd --- /dev/null +++ b/naked-objects/dom/src/main/java/domainapp/dom/app/homepage/HomePageViewModel.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package domainapp.dom.app.homepage; + +import java.util.List; + +import org.apache.isis.applib.annotation.ViewModel; + +import domainapp.dom.modules.simple.SimpleObject; +import domainapp.dom.modules.simple.SimpleObjects; + +@ViewModel +public class HomePageViewModel { + + // endregion + + // region > injected services + + @javax.inject.Inject + SimpleObjects simpleObjects; + + // endregion + + // region > title + public String title() { + return getObjects().size() + " objects"; + } + + // endregion + + // region > object (collection) + @org.apache.isis.applib.annotation.HomePage + public List getObjects() { + return simpleObjects.listAll(); + } + +} diff --git a/naked-objects/dom/src/main/java/domainapp/dom/app/homepage/HomePageViewModel.layout.json b/naked-objects/dom/src/main/java/domainapp/dom/app/homepage/HomePageViewModel.layout.json new file mode 100644 index 000000000..34f78e0c3 --- /dev/null +++ b/naked-objects/dom/src/main/java/domainapp/dom/app/homepage/HomePageViewModel.layout.json @@ -0,0 +1,43 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +{ + "columns": [ + { + "span": 0, + "memberGroups": {} + }, + { + "span": 0, + "memberGroups": {} + }, + { + "span": 0, + "memberGroups": {} + }, + { + "span": 12, + "collections": { + "objects": { + "collectionLayout": { + "render": "EAGERLY" + } + } + } + } + ], + "actions": {} +} \ No newline at end of file diff --git a/naked-objects/dom/src/main/java/domainapp/dom/app/homepage/HomePageViewModel.png b/naked-objects/dom/src/main/java/domainapp/dom/app/homepage/HomePageViewModel.png new file mode 100644 index 000000000..cb03785f7 Binary files /dev/null and b/naked-objects/dom/src/main/java/domainapp/dom/app/homepage/HomePageViewModel.png differ diff --git a/naked-objects/dom/src/main/java/domainapp/dom/modules/simple/SimpleObject.java b/naked-objects/dom/src/main/java/domainapp/dom/modules/simple/SimpleObject.java new file mode 100644 index 000000000..bbbd54b00 --- /dev/null +++ b/naked-objects/dom/src/main/java/domainapp/dom/modules/simple/SimpleObject.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package domainapp.dom.modules.simple; + +import javax.jdo.JDOHelper; +import javax.jdo.annotations.IdentityType; +import javax.jdo.annotations.VersionStrategy; + +import org.apache.isis.applib.DomainObjectContainer; +import org.apache.isis.applib.Identifier; +import org.apache.isis.applib.annotation.Action; +import org.apache.isis.applib.annotation.BookmarkPolicy; +import org.apache.isis.applib.annotation.DomainObject; +import org.apache.isis.applib.annotation.DomainObjectLayout; +import org.apache.isis.applib.annotation.Editing; +import org.apache.isis.applib.annotation.Parameter; +import org.apache.isis.applib.annotation.ParameterLayout; +import org.apache.isis.applib.annotation.Property; +import org.apache.isis.applib.annotation.Title; +import org.apache.isis.applib.services.eventbus.ActionDomainEvent; +import org.apache.isis.applib.services.i18n.TranslatableString; +import org.apache.isis.applib.util.ObjectContracts; + +@javax.jdo.annotations.PersistenceCapable(identityType = IdentityType.DATASTORE, schema = "simple", + table = "SimpleObject") +@javax.jdo.annotations.DatastoreIdentity( + strategy = javax.jdo.annotations.IdGeneratorStrategy.IDENTITY, column = "id") +@javax.jdo.annotations.Version(strategy = VersionStrategy.VERSION_NUMBER, column = "version") +@javax.jdo.annotations.Queries({ + @javax.jdo.annotations.Query(name = "find", language = "JDOQL", value = "SELECT " + + "FROM domainapp.dom.modules.simple.SimpleObject "), + @javax.jdo.annotations.Query(name = "findByName", language = "JDOQL", value = "SELECT " + + "FROM domainapp.dom.modules.simple.SimpleObject " + "WHERE name.indexOf(:name) >= 0 ")}) +@javax.jdo.annotations.Unique(name = "SimpleObject_name_UNQ", members = {"name"}) +@DomainObject +@DomainObjectLayout(bookmarking = BookmarkPolicy.AS_ROOT, cssClassFa = "fa-flag") +public class SimpleObject implements Comparable { + + // endregion + + // region > name (property) + + private String name; + + // region > identificatiom + public TranslatableString title() { + return TranslatableString.tr("Object: {name}", "name", getName()); + } + + @javax.jdo.annotations.Column(allowsNull = "false", length = 40) + @Title(sequence = "1") + @Property(editing = Editing.DISABLED) + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + // endregion + + // region > updateName (action) + + public static class UpdateNameDomainEvent extends ActionDomainEvent { + public UpdateNameDomainEvent(final SimpleObject source, final Identifier identifier, + final Object... arguments) { + super(source, identifier, arguments); + } + } + + @Action(domainEvent = UpdateNameDomainEvent.class) + public SimpleObject updateName( + @Parameter(maxLength = 40) @ParameterLayout(named = "New name") final String name) { + setName(name); + return this; + } + + public String default0UpdateName() { + return getName(); + } + + public TranslatableString validateUpdateName(final String name) { + return name.contains("!") ? TranslatableString.tr("Exclamation mark is not allowed") : null; + } + + // endregion + + // region > version (derived property) + public Long getVersionSequence() { + return (Long) JDOHelper.getVersion(this); + } + + // endregion + + // region > compareTo + + @Override + public int compareTo(final SimpleObject other) { + return ObjectContracts.compare(this, other, "name"); + } + + // endregion + + // region > injected services + + @javax.inject.Inject + @SuppressWarnings("unused") + private DomainObjectContainer container; + + // endregion + + +} diff --git a/naked-objects/dom/src/main/java/domainapp/dom/modules/simple/SimpleObject.layout.json b/naked-objects/dom/src/main/java/domainapp/dom/modules/simple/SimpleObject.layout.json new file mode 100644 index 000000000..3d5e23f7c --- /dev/null +++ b/naked-objects/dom/src/main/java/domainapp/dom/modules/simple/SimpleObject.layout.json @@ -0,0 +1,56 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +{ + "columns": [ + { + "span": 6, + "memberGroups": { + "General": { + "members": { + "name": { + "actions": { + "updateName": { + "actionLayout": { + "position": "BOTTOM" + } + } + } + }, + "versionSequence": { + "propertyLayout": { + "name": "version" + } + } + } + } + } + }, + { + "span": 0, + "memberGroups": {} + }, + { + "span": 0, + "memberGroups": {} + }, + { + "span": 6, + "collections": {} + } + ], + "actions": {} +} \ No newline at end of file diff --git a/naked-objects/dom/src/main/java/domainapp/dom/modules/simple/SimpleObject.png b/naked-objects/dom/src/main/java/domainapp/dom/modules/simple/SimpleObject.png new file mode 100644 index 000000000..0bd6f5756 Binary files /dev/null and b/naked-objects/dom/src/main/java/domainapp/dom/modules/simple/SimpleObject.png differ diff --git a/naked-objects/dom/src/main/java/domainapp/dom/modules/simple/SimpleObjects.java b/naked-objects/dom/src/main/java/domainapp/dom/modules/simple/SimpleObjects.java new file mode 100644 index 000000000..5ebad0159 --- /dev/null +++ b/naked-objects/dom/src/main/java/domainapp/dom/modules/simple/SimpleObjects.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package domainapp.dom.modules.simple; + +import java.util.List; + +import org.apache.isis.applib.DomainObjectContainer; +import org.apache.isis.applib.Identifier; +import org.apache.isis.applib.annotation.Action; +import org.apache.isis.applib.annotation.ActionLayout; +import org.apache.isis.applib.annotation.BookmarkPolicy; +import org.apache.isis.applib.annotation.DomainService; +import org.apache.isis.applib.annotation.DomainServiceLayout; +import org.apache.isis.applib.annotation.MemberOrder; +import org.apache.isis.applib.annotation.ParameterLayout; +import org.apache.isis.applib.annotation.SemanticsOf; +import org.apache.isis.applib.query.QueryDefault; +import org.apache.isis.applib.services.eventbus.ActionDomainEvent; +import org.apache.isis.applib.services.i18n.TranslatableString; + +@DomainService(repositoryFor = SimpleObject.class) +@DomainServiceLayout(menuOrder = "10") +public class SimpleObjects { + // endregion + + // region > injected services + + @javax.inject.Inject + DomainObjectContainer container; + + // endregion + + // region > title + public TranslatableString title() { + return TranslatableString.tr("Simple Objects"); + } + + // endregion + + // region > listAll (action) + @Action(semantics = SemanticsOf.SAFE) + @ActionLayout(bookmarking = BookmarkPolicy.AS_ROOT) + @MemberOrder(sequence = "1") + public List listAll() { + return container.allInstances(SimpleObject.class); + } + + // endregion + + // region > findByName (action) + @Action(semantics = SemanticsOf.SAFE) + @ActionLayout(bookmarking = BookmarkPolicy.AS_ROOT) + @MemberOrder(sequence = "2") + public List findByName(@ParameterLayout(named = "Name") final String name) { + return container.allMatches(new QueryDefault<>(SimpleObject.class, "findByName", "name", name)); + } + + // endregion + + // region > create (action) + public static class CreateDomainEvent extends ActionDomainEvent { + public CreateDomainEvent(final SimpleObjects source, final Identifier identifier, + final Object... arguments) { + super(source, identifier, arguments); + } + } + + /** + * Create simple object + */ + @Action(domainEvent = CreateDomainEvent.class) + @MemberOrder(sequence = "3") + public SimpleObject create(@ParameterLayout(named = "Name") final String name) { + final SimpleObject obj = container.newTransientInstance(SimpleObject.class); + obj.setName(name); + container.persistIfNotAlready(obj); + return obj; + } + +} diff --git a/naked-objects/dom/src/test/java/domainapp/dom/modules/simple/SimpleObjectTest.java b/naked-objects/dom/src/test/java/domainapp/dom/modules/simple/SimpleObjectTest.java new file mode 100644 index 000000000..fc62239c2 --- /dev/null +++ b/naked-objects/dom/src/test/java/domainapp/dom/modules/simple/SimpleObjectTest.java @@ -0,0 +1,47 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package domainapp.dom.modules.simple; + +import org.junit.Before; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SimpleObjectTest { + + SimpleObject simpleObject; + + @Before + public void setUp() throws Exception { + simpleObject = new SimpleObject(); + } + + public static class Name extends SimpleObjectTest { + + @Test + public void happyCase() throws Exception { + // given + String name = "Foobar"; + assertThat(simpleObject.getName()).isNull(); + + // when + simpleObject.setName(name); + + // then + assertThat(simpleObject.getName()).isEqualTo(name); + } + } + +} diff --git a/naked-objects/dom/src/test/java/domainapp/dom/modules/simple/SimpleObjectsTest.java b/naked-objects/dom/src/test/java/domainapp/dom/modules/simple/SimpleObjectsTest.java new file mode 100644 index 000000000..47cad61b8 --- /dev/null +++ b/naked-objects/dom/src/test/java/domainapp/dom/modules/simple/SimpleObjectsTest.java @@ -0,0 +1,102 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package domainapp.dom.modules.simple; + +import java.util.List; + +import com.google.common.collect.Lists; + +import org.jmock.Expectations; +import org.jmock.Sequence; +import org.jmock.auto.Mock; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import org.apache.isis.applib.DomainObjectContainer; +import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2; +import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SimpleObjectsTest { + + @Rule + public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES); + + @Mock + DomainObjectContainer mockContainer; + + SimpleObjects simpleObjects; + + @Before + public void setUp() throws Exception { + simpleObjects = new SimpleObjects(); + simpleObjects.container = mockContainer; + } + + public static class Create extends SimpleObjectsTest { + + @Test + public void happyCase() throws Exception { + + // given + final SimpleObject simpleObject = new SimpleObject(); + + final Sequence seq = context.sequence("create"); + context.checking(new Expectations() { + { + oneOf(mockContainer).newTransientInstance(SimpleObject.class); + inSequence(seq); + will(returnValue(simpleObject)); + + oneOf(mockContainer).persistIfNotAlready(simpleObject); + inSequence(seq); + } + }); + + // when + final SimpleObject obj = simpleObjects.create("Foobar"); + + // then + assertThat(obj).isEqualTo(simpleObject); + assertThat(obj.getName()).isEqualTo("Foobar"); + } + + } + + public static class ListAll extends SimpleObjectsTest { + + @Test + public void happyCase() throws Exception { + + // given + final List all = Lists.newArrayList(); + + context.checking(new Expectations() { + { + oneOf(mockContainer).allInstances(SimpleObject.class); + will(returnValue(all)); + } + }); + + // when + final List list = simpleObjects.listAll(); + + // then + assertThat(list).isEqualTo(all); + } + } +} diff --git a/naked-objects/etc/naked-objects.png b/naked-objects/etc/naked-objects.png new file mode 100644 index 000000000..eda8439a2 Binary files /dev/null and b/naked-objects/etc/naked-objects.png differ diff --git a/naked-objects/etc/naked-objects.ucls b/naked-objects/etc/naked-objects.ucls new file mode 100644 index 000000000..8e9afac6d --- /dev/null +++ b/naked-objects/etc/naked-objects.ucls @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/naked-objects/fixture/pom.xml b/naked-objects/fixture/pom.xml new file mode 100644 index 000000000..46b281468 --- /dev/null +++ b/naked-objects/fixture/pom.xml @@ -0,0 +1,31 @@ + + + + 4.0.0 + + + com.iluwatar + naked-objects + 1.13.0-SNAPSHOT + + + naked-objects-fixture + + + + ${project.groupId} + naked-objects-dom + + + + diff --git a/naked-objects/fixture/src/main/java/domainapp/fixture/DomainAppFixturesProvider.java b/naked-objects/fixture/src/main/java/domainapp/fixture/DomainAppFixturesProvider.java new file mode 100644 index 000000000..ccc11f2b8 --- /dev/null +++ b/naked-objects/fixture/src/main/java/domainapp/fixture/DomainAppFixturesProvider.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package domainapp.fixture; + +import org.apache.isis.applib.annotation.DomainService; +import org.apache.isis.applib.annotation.NatureOfService; +import org.apache.isis.applib.fixturescripts.FixtureScripts; +import org.apache.isis.applib.services.fixturespec.FixtureScriptsSpecification; +import org.apache.isis.applib.services.fixturespec.FixtureScriptsSpecificationProvider; + +import domainapp.fixture.scenarios.RecreateSimpleObjects; + +/** + * Specifies where to find fixtures, and other settings. + */ +@DomainService(nature = NatureOfService.DOMAIN) +public class DomainAppFixturesProvider implements FixtureScriptsSpecificationProvider { + @Override + public FixtureScriptsSpecification getSpecification() { + return FixtureScriptsSpecification.builder(DomainAppFixturesProvider.class) + .with(FixtureScripts.MultipleExecutionStrategy.EXECUTE) + .withRunScriptDefault(RecreateSimpleObjects.class) + .withRunScriptDropDown(FixtureScriptsSpecification.DropDownPolicy.CHOICES) + .withRecreate(RecreateSimpleObjects.class).build(); + } +} diff --git a/naked-objects/fixture/src/main/java/domainapp/fixture/modules/simple/SimpleObjectCreate.java b/naked-objects/fixture/src/main/java/domainapp/fixture/modules/simple/SimpleObjectCreate.java new file mode 100644 index 000000000..f0617fea2 --- /dev/null +++ b/naked-objects/fixture/src/main/java/domainapp/fixture/modules/simple/SimpleObjectCreate.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package domainapp.fixture.modules.simple; + +import org.apache.isis.applib.fixturescripts.FixtureScript; + +import domainapp.dom.modules.simple.SimpleObject; +import domainapp.dom.modules.simple.SimpleObjects; + +public class SimpleObjectCreate extends FixtureScript { + + // endregion + + + // region > simpleObject (output) + private SimpleObject simpleObject; + + @javax.inject.Inject + private SimpleObjects simpleObjects; + + // region > name (input) + private String name; + + /** + * Name of the object (required) + */ + public String getName() { + return name; + } + + public SimpleObjectCreate setName(final String name) { + this.name = name; + return this; + } + + /** + * The created simple object (output). + */ + public SimpleObject getSimpleObject() { + return simpleObject; + } + + // endregion + + @Override + protected void execute(final ExecutionContext ec) { + + String paramName = checkParam("name", ec, String.class); + + this.simpleObject = wrap(simpleObjects).create(paramName); + + // also make available to UI + ec.addResult(this, simpleObject); + } + +} diff --git a/naked-objects/fixture/src/main/java/domainapp/fixture/modules/simple/SimpleObjectsTearDown.java b/naked-objects/fixture/src/main/java/domainapp/fixture/modules/simple/SimpleObjectsTearDown.java new file mode 100644 index 000000000..7000bf4c0 --- /dev/null +++ b/naked-objects/fixture/src/main/java/domainapp/fixture/modules/simple/SimpleObjectsTearDown.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package domainapp.fixture.modules.simple; + +import org.apache.isis.applib.fixturescripts.FixtureScript; +import org.apache.isis.applib.services.jdosupport.IsisJdoSupport; + +public class SimpleObjectsTearDown extends FixtureScript { + + @javax.inject.Inject + private IsisJdoSupport isisJdoSupport; + + @Override + protected void execute(ExecutionContext executionContext) { + isisJdoSupport.executeUpdate("delete from \"simple\".\"SimpleObject\""); + } + +} diff --git a/naked-objects/fixture/src/main/java/domainapp/fixture/scenarios/RecreateSimpleObjects.java b/naked-objects/fixture/src/main/java/domainapp/fixture/scenarios/RecreateSimpleObjects.java new file mode 100644 index 000000000..62ad0405a --- /dev/null +++ b/naked-objects/fixture/src/main/java/domainapp/fixture/scenarios/RecreateSimpleObjects.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package domainapp.fixture.scenarios; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import com.google.common.collect.Lists; + +import org.apache.isis.applib.fixturescripts.FixtureScript; + +import domainapp.dom.modules.simple.SimpleObject; +import domainapp.fixture.modules.simple.SimpleObjectCreate; +import domainapp.fixture.modules.simple.SimpleObjectsTearDown; + +public class RecreateSimpleObjects extends FixtureScript { + + public final List names = Collections.unmodifiableList(Arrays.asList("Foo", "Bar", "Baz", + "Frodo", "Froyo", "Fizz", "Bip", "Bop", "Bang", "Boo")); + + // region > number (optional input) + private Integer number; + + // endregion + + // region > simpleObjects (output) + private final List simpleObjects = Lists.newArrayList(); + + public RecreateSimpleObjects() { + withDiscoverability(Discoverability.DISCOVERABLE); + } + + /** + * The number of objects to create, up to 10; optional, defaults to 3. + */ + public Integer getNumber() { + return number; + } + + public RecreateSimpleObjects setNumber(final Integer number) { + this.number = number; + return this; + } + + /** + * The simpleobjects created by this fixture (output). + */ + public List getSimpleObjects() { + return simpleObjects; + } + + // endregion + + @Override + protected void execute(final ExecutionContext ec) { + + // defaults + final int paramNumber = defaultParam("number", ec, 3); + + // validate + if (paramNumber < 0 || paramNumber > names.size()) { + throw new IllegalArgumentException(String.format("number must be in range [0,%d)", + names.size())); + } + + // + // execute + // + ec.executeChild(this, new SimpleObjectsTearDown()); + + for (int i = 0; i < paramNumber; i++) { + final SimpleObjectCreate fs = new SimpleObjectCreate().setName(names.get(i)); + ec.executeChild(this, fs.getName(), fs); + simpleObjects.add(fs.getSimpleObject()); + } + } +} diff --git a/naked-objects/integtests/.gitignore b/naked-objects/integtests/.gitignore new file mode 100644 index 000000000..88dfbbca9 --- /dev/null +++ b/naked-objects/integtests/.gitignore @@ -0,0 +1 @@ +/translations.pot diff --git a/naked-objects/integtests/logging.properties b/naked-objects/integtests/logging.properties new file mode 100644 index 000000000..b55249569 --- /dev/null +++ b/naked-objects/integtests/logging.properties @@ -0,0 +1,111 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +# +# Isis uses log4j is used to provide system logging +# +log4j.rootCategory=INFO, Console + +# The console appender +log4j.appender.Console=org.apache.log4j.ConsoleAppender +log4j.appender.Console.target=System.out +log4j.appender.Console.layout=org.apache.log4j.PatternLayout +log4j.appender.Console.layout.ConversionPattern=%d{ABSOLUTE} [%-20c{1} %-10t %-5p] %m%n + +log4j.appender.File=org.apache.log4j.RollingFileAppender +log4j.appender.File.file=isis.log +log4j.appender.File.append=false +log4j.appender.File.layout=org.apache.log4j.PatternLayout +log4j.appender.File.layout.ConversionPattern=%d [%-20c{1} %-10t %-5p] %m%n + +log4j.appender.translations-po=org.apache.log4j.FileAppender +log4j.appender.translations-po.File=./translations.pot +log4j.appender.translations-po.Append=false +log4j.appender.translations-po.layout=org.apache.log4j.PatternLayout +log4j.appender.translations-po.layout.ConversionPattern=%m%n + +! turn on the internal log4j debugging flag so we can see what it is doing +#log4j.debug=true + + +# DataNucleus +# the first two log the DML and DDL (if set to DEBUG) +log4j.logger.DataNucleus.Datastore.Native=WARN, Console +log4j.logger.DataNucleus.Datastore.Schema=DEBUG, Console +# the remainder can probably be left to WARN +log4j.logger.DataNucleus.Persistence=WARN, Console +log4j.logger.DataNucleus.Transaction=WARN, Console +log4j.logger.DataNucleus.Connection=WARN, Console +log4j.logger.DataNucleus.Query=WARN, Console +log4j.logger.DataNucleus.Cache=WARN, Console +log4j.logger.DataNucleus.MetaData=WARN, Console +log4j.logger.DataNucleus.Datastore=WARN, Console +log4j.logger.DataNucleus.Datastore.Persist=WARN, Console +log4j.logger.DataNucleus.Datastore.Retrieve=WARN, Console +log4j.logger.DataNucleus.General=WARN, Console +log4j.logger.DataNucleus.Lifecycle=WARN, Console +log4j.logger.DataNucleus.ValueGeneration=WARN, Console +log4j.logger.DataNucleus.Enhancer=WARN, Console +log4j.logger.DataNucleus.SchemaTool=ERROR, Console +log4j.logger.DataNucleus.JDO=WARN, Console +log4j.logger.DataNucleus.JPA=ERROR, Console +log4j.logger.DataNucleus.JCA=WARN, Console +log4j.logger.DataNucleus.IDE=ERROR, Console + +log4j.additivity.DataNucleus.Datastore.Native=false +log4j.additivity.DataNucleus.Datastore.Schema=false +log4j.additivity.DataNucleus.Datastore.Persistence=false +log4j.additivity.DataNucleus.Datastore.Transaction=false +log4j.additivity.DataNucleus.Datastore.Connection=false +log4j.additivity.DataNucleus.Datastore.Query=false +log4j.additivity.DataNucleus.Datastore.Cache=false +log4j.additivity.DataNucleus.Datastore.MetaData=false +log4j.additivity.DataNucleus.Datastore.Datastore=false +log4j.additivity.DataNucleus.Datastore.Datastore.Persist=false +log4j.additivity.DataNucleus.Datastore.Datastore.Retrieve=false +log4j.additivity.DataNucleus.Datastore.General=false +log4j.additivity.DataNucleus.Datastore.Lifecycle=false +log4j.additivity.DataNucleus.Datastore.ValueGeneration=false +log4j.additivity.DataNucleus.Datastore.Enhancer=false +log4j.additivity.DataNucleus.Datastore.SchemaTool=false +log4j.additivity.DataNucleus.Datastore.JDO=false +log4j.additivity.DataNucleus.Datastore.JPA=false +log4j.additivity.DataNucleus.Datastore.JCA=false +log4j.additivity.DataNucleus.Datastore.IDE=false + + + + +# if using log4jdbc-remix as JDBC driver +#log4j.logger.jdbc.sqlonly=DEBUG, sql, Console +#log4j.additivity.jdbc.sqlonly=false +#log4j.logger.jdbc.resultsettable=DEBUG, jdbc, Console +#log4j.additivity.jdbc.resultsettable=false + +#log4j.logger.jdbc.audit=WARN,jdbc, Console +#log4j.additivity.jdbc.audit=false +#log4j.logger.jdbc.resultset=WARN,jdbc +#log4j.additivity.jdbc.resultset=false +#log4j.logger.jdbc.sqltiming=WARN,sqltiming +#log4j.additivity.jdbc.sqltiming=false +#log4j.logger.jdbc.connection=FATAL,connection +#log4j.additivity.jdbc.connection=false + + +log4j.logger.org.apache.isis.core.runtime.services.i18n.po.PoWriter=INFO,translations-po +log4j.additivity.org.apache.isis.core.runtime.services.i18n.po.PotWriter=false diff --git a/naked-objects/integtests/pom.xml b/naked-objects/integtests/pom.xml new file mode 100644 index 000000000..dccaf64a3 --- /dev/null +++ b/naked-objects/integtests/pom.xml @@ -0,0 +1,121 @@ + + + + 4.0.0 + + + com.iluwatar + naked-objects + 1.13.0-SNAPSHOT + + + naked-objects-integtests + + + + + src/test/resources + + + src/test/java + + ** + + + **/*.java + + + + + + + + + ${project.groupId} + naked-objects-fixture + + + + org.apache.isis.core + isis-core-unittestsupport + + + + org.apache.isis.core + isis-core-integtestsupport + + + org.apache.isis.core + isis-core-specsupport + + + + org.hamcrest + hamcrest-library + + + + org.apache.isis.core + isis-core-wrapper + + + org.apache.isis.core + isis-core-runtime + + + + org.assertj + assertj-core + test + + + + org.hsqldb + hsqldb + + + + + + + + + diff --git a/naked-objects/integtests/src/test/java/domainapp/integtests/bootstrap/SimpleAppSystemInitializer.java b/naked-objects/integtests/src/test/java/domainapp/integtests/bootstrap/SimpleAppSystemInitializer.java new file mode 100644 index 000000000..b7c76d0ed --- /dev/null +++ b/naked-objects/integtests/src/test/java/domainapp/integtests/bootstrap/SimpleAppSystemInitializer.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package domainapp.integtests.bootstrap; + +import org.apache.isis.core.commons.config.IsisConfiguration; +import org.apache.isis.core.integtestsupport.IsisSystemForTest; +import org.apache.isis.objectstore.jdo.datanucleus.DataNucleusPersistenceMechanismInstaller; +import org.apache.isis.objectstore.jdo.datanucleus.IsisConfigurationForJdoIntegTests; + +public final class SimpleAppSystemInitializer { + + private SimpleAppSystemInitializer() { + } + + /** + * Init test system + */ + public static void initIsft() { + IsisSystemForTest isft = IsisSystemForTest.getElseNull(); + if (isft == null) { + isft = new SimpleAppSystemBuilder().build().setUpSystem(); + IsisSystemForTest.set(isft); + } + } + + private static class SimpleAppSystemBuilder extends IsisSystemForTest.Builder { + + public SimpleAppSystemBuilder() { + withLoggingAt(org.apache.log4j.Level.INFO); + with(testConfiguration()); + with(new DataNucleusPersistenceMechanismInstaller()); + + // services annotated with @DomainService + withServicesIn("domainapp"); + } + + private static IsisConfiguration testConfiguration() { + final IsisConfigurationForJdoIntegTests testConfiguration = + new IsisConfigurationForJdoIntegTests(); + + testConfiguration.addRegisterEntitiesPackagePrefix("domainapp.dom.modules"); + return testConfiguration; + } + } +} diff --git a/naked-objects/integtests/src/test/java/domainapp/integtests/specglue/BootstrappingGlue.java b/naked-objects/integtests/src/test/java/domainapp/integtests/specglue/BootstrappingGlue.java new file mode 100644 index 000000000..190e1f5bb --- /dev/null +++ b/naked-objects/integtests/src/test/java/domainapp/integtests/specglue/BootstrappingGlue.java @@ -0,0 +1,39 @@ +/** + * O * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package domainapp.integtests.specglue; + +import org.apache.isis.core.specsupport.scenarios.ScenarioExecutionScope; +import org.apache.isis.core.specsupport.specs.CukeGlueAbstract; + +import cucumber.api.java.After; +import cucumber.api.java.Before; +import domainapp.integtests.bootstrap.SimpleAppSystemInitializer; + +public class BootstrappingGlue extends CukeGlueAbstract { + + @Before(value = {"@integration"}, order = 100) + public void beforeScenarioIntegrationScope() { + org.apache.log4j.PropertyConfigurator.configure("logging.properties"); + SimpleAppSystemInitializer.initIsft(); + + before(ScenarioExecutionScope.INTEGRATION); + } + + @After + public void afterScenario(cucumber.api.Scenario sc) { + assertMocksSatisfied(); + after(sc); + } +} diff --git a/naked-objects/integtests/src/test/java/domainapp/integtests/specglue/CatalogOfFixturesGlue.java b/naked-objects/integtests/src/test/java/domainapp/integtests/specglue/CatalogOfFixturesGlue.java new file mode 100644 index 000000000..2fcb7cca7 --- /dev/null +++ b/naked-objects/integtests/src/test/java/domainapp/integtests/specglue/CatalogOfFixturesGlue.java @@ -0,0 +1,28 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package domainapp.integtests.specglue; + +import org.apache.isis.core.specsupport.specs.CukeGlueAbstract; + +import cucumber.api.java.Before; +import domainapp.fixture.scenarios.RecreateSimpleObjects; + +public class CatalogOfFixturesGlue extends CukeGlueAbstract { + + @Before(value = {"@integration", "@SimpleObjectsFixture"}, order = 20000) + public void integrationFixtures() throws Throwable { + scenarioExecution().install(new RecreateSimpleObjects()); + } +} diff --git a/naked-objects/integtests/src/test/java/domainapp/integtests/specglue/modules/simple/SimpleObjectGlue.java b/naked-objects/integtests/src/test/java/domainapp/integtests/specglue/modules/simple/SimpleObjectGlue.java new file mode 100644 index 000000000..2ea375b4a --- /dev/null +++ b/naked-objects/integtests/src/test/java/domainapp/integtests/specglue/modules/simple/SimpleObjectGlue.java @@ -0,0 +1,48 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package domainapp.integtests.specglue.modules.simple; + +import java.util.List; +import java.util.UUID; + +import org.apache.isis.core.specsupport.specs.CukeGlueAbstract; + +import cucumber.api.java.en.Given; +import cucumber.api.java.en.When; +import domainapp.dom.modules.simple.SimpleObject; +import domainapp.dom.modules.simple.SimpleObjects; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +public class SimpleObjectGlue extends CukeGlueAbstract { + + @Given("^there are.* (\\d+) simple objects$") + public void thereAreNumSimpleObjects(int n) throws Throwable { + try { + final List findAll = service(SimpleObjects.class).listAll(); + assertThat(findAll.size(), is(n)); + putVar("list", "all", findAll); + + } finally { + assertMocksSatisfied(); + } + } + + @When("^I create a new simple object$") + public void createNewSimpleObject() throws Throwable { + service(SimpleObjects.class).create(UUID.randomUUID().toString()); + } + +} diff --git a/naked-objects/integtests/src/test/java/domainapp/integtests/specs/RunSpecs.java b/naked-objects/integtests/src/test/java/domainapp/integtests/specs/RunSpecs.java new file mode 100644 index 000000000..8a842a0f3 --- /dev/null +++ b/naked-objects/integtests/src/test/java/domainapp/integtests/specs/RunSpecs.java @@ -0,0 +1,32 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package domainapp.integtests.specs; + +import org.junit.runner.RunWith; + +import cucumber.api.CucumberOptions; +import cucumber.api.junit.Cucumber; + + +/** + * Runs scenarios in all .feature files (this package and any subpackages). + */ +@RunWith(Cucumber.class) +@CucumberOptions(format = {"html:target/cucumber-html-report", "json:target/cucumber.json"}, + glue = {"classpath:domainapp.integtests.specglue"}, strict = true, tags = {"~@backlog", + "~@ignore"}) +public class RunSpecs { + // intentionally empty +} diff --git a/naked-objects/integtests/src/test/java/domainapp/integtests/specs/modules/simple/SimpleObjectSpec_listAllAndCreate.feature b/naked-objects/integtests/src/test/java/domainapp/integtests/specs/modules/simple/SimpleObjectSpec_listAllAndCreate.feature new file mode 100644 index 000000000..346aa2562 --- /dev/null +++ b/naked-objects/integtests/src/test/java/domainapp/integtests/specs/modules/simple/SimpleObjectSpec_listAllAndCreate.feature @@ -0,0 +1,26 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +@SimpleObjectsFixture +Feature: List and Create New Simple Objects + + @integration + Scenario: Existing simple objects can be listed and new ones created + Given there are initially 3 simple objects + When I create a new simple object + Then there are 4 simple objects + + \ No newline at end of file diff --git a/naked-objects/integtests/src/test/java/domainapp/integtests/tests/SimpleAppIntegTest.java b/naked-objects/integtests/src/test/java/domainapp/integtests/tests/SimpleAppIntegTest.java new file mode 100644 index 000000000..7a7ad91b2 --- /dev/null +++ b/naked-objects/integtests/src/test/java/domainapp/integtests/tests/SimpleAppIntegTest.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package domainapp.integtests.tests; + +import org.junit.BeforeClass; + +import org.apache.isis.core.integtestsupport.IntegrationTestAbstract; +import org.apache.isis.core.integtestsupport.scenarios.ScenarioExecutionForIntegration; + +import domainapp.integtests.bootstrap.SimpleAppSystemInitializer; + +public abstract class SimpleAppIntegTest extends IntegrationTestAbstract { + + @BeforeClass + public static void initClass() { + org.apache.log4j.PropertyConfigurator.configure("logging.properties"); + SimpleAppSystemInitializer.initIsft(); + + // instantiating will install onto ThreadLocal + new ScenarioExecutionForIntegration(); + } + +} diff --git a/naked-objects/integtests/src/test/java/domainapp/integtests/tests/modules/simple/SimpleObjectIntegTest.java b/naked-objects/integtests/src/test/java/domainapp/integtests/tests/modules/simple/SimpleObjectIntegTest.java new file mode 100644 index 000000000..872aff7a3 --- /dev/null +++ b/naked-objects/integtests/src/test/java/domainapp/integtests/tests/modules/simple/SimpleObjectIntegTest.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package domainapp.integtests.tests.modules.simple; + +import javax.inject.Inject; + +import org.junit.Before; +import org.junit.Test; + +import org.apache.isis.applib.DomainObjectContainer; +import org.apache.isis.applib.fixturescripts.FixtureScripts; +import org.apache.isis.applib.services.wrapper.DisabledException; +import org.apache.isis.applib.services.wrapper.InvalidException; + +import domainapp.dom.modules.simple.SimpleObject; +import domainapp.fixture.scenarios.RecreateSimpleObjects; +import domainapp.integtests.tests.SimpleAppIntegTest; +import static org.assertj.core.api.Assertions.assertThat; + +public class SimpleObjectIntegTest extends SimpleAppIntegTest { + + @Inject + FixtureScripts fixtureScripts; + + RecreateSimpleObjects fs; + SimpleObject simpleObjectPojo; + SimpleObject simpleObjectWrapped; + + @Before + public void setUp() throws Exception { + // given + fs = new RecreateSimpleObjects().setNumber(1); + fixtureScripts.runFixtureScript(fs, null); + + simpleObjectPojo = fs.getSimpleObjects().get(0); + + assertThat(simpleObjectPojo).isNotNull(); + simpleObjectWrapped = wrap(simpleObjectPojo); + } + + public static class Name extends SimpleObjectIntegTest { + + @Test + public void accessible() throws Exception { + // when + final String name = simpleObjectWrapped.getName(); + // then + assertThat(name).isEqualTo(fs.names.get(0)); + } + + @Test + public void cannotBeUpdatedDirectly() throws Exception { + + // expect + expectedExceptions.expect(DisabledException.class); + + // when + simpleObjectWrapped.setName("new name"); + } + } + + public static class UpdateName extends SimpleObjectIntegTest { + + @Test + public void happyCase() throws Exception { + + // when + simpleObjectWrapped.updateName("new name"); + + // then + assertThat(simpleObjectWrapped.getName()).isEqualTo("new name"); + } + + @Test + public void failsValidation() throws Exception { + + // expect + expectedExceptions.expect(InvalidException.class); + expectedExceptions.expectMessage("Exclamation mark is not allowed"); + + // when + simpleObjectWrapped.updateName("new name!"); + } + } + + public static class Title extends SimpleObjectIntegTest { + + @Inject + DomainObjectContainer container; + + @Test + public void interpolatesName() throws Exception { + + // given + final String name = simpleObjectWrapped.getName(); + + // when + final String title = container.titleOf(simpleObjectWrapped); + + // then + assertThat(title).isEqualTo("Object: " + name); + } + } +} \ No newline at end of file diff --git a/naked-objects/integtests/src/test/java/domainapp/integtests/tests/modules/simple/SimpleObjectsIntegTest.java b/naked-objects/integtests/src/test/java/domainapp/integtests/tests/modules/simple/SimpleObjectsIntegTest.java new file mode 100644 index 000000000..332213542 --- /dev/null +++ b/naked-objects/integtests/src/test/java/domainapp/integtests/tests/modules/simple/SimpleObjectsIntegTest.java @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package domainapp.integtests.tests.modules.simple; + +import java.sql.SQLIntegrityConstraintViolationException; +import java.util.List; + +import javax.inject.Inject; + +import com.google.common.base.Throwables; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; +import org.junit.Test; + +import org.apache.isis.applib.fixturescripts.FixtureScript; +import org.apache.isis.applib.fixturescripts.FixtureScripts; + +import domainapp.dom.modules.simple.SimpleObject; +import domainapp.dom.modules.simple.SimpleObjects; +import domainapp.fixture.modules.simple.SimpleObjectsTearDown; +import domainapp.fixture.scenarios.RecreateSimpleObjects; +import domainapp.integtests.tests.SimpleAppIntegTest; +import static org.assertj.core.api.Assertions.assertThat; + +public class SimpleObjectsIntegTest extends SimpleAppIntegTest { + + @Inject + FixtureScripts fixtureScripts; + @Inject + SimpleObjects simpleObjects; + + public static class ListAll extends SimpleObjectsIntegTest { + + @Test + public void happyCase() throws Exception { + + // given + RecreateSimpleObjects fs = new RecreateSimpleObjects(); + fixtureScripts.runFixtureScript(fs, null); + nextTransaction(); + + // when + final List all = wrap(simpleObjects).listAll(); + + // then + assertThat(all).hasSize(fs.getSimpleObjects().size()); + + SimpleObject simpleObject = wrap(all.get(0)); + assertThat(simpleObject.getName()).isEqualTo(fs.getSimpleObjects().get(0).getName()); + } + + @Test + public void whenNone() throws Exception { + + // given + FixtureScript fs = new SimpleObjectsTearDown(); + fixtureScripts.runFixtureScript(fs, null); + nextTransaction(); + + // when + final List all = wrap(simpleObjects).listAll(); + + // then + assertThat(all).hasSize(0); + } + } + + public static class Create extends SimpleObjectsIntegTest { + + @Test + public void happyCase() throws Exception { + + // given + FixtureScript fs = new SimpleObjectsTearDown(); + fixtureScripts.runFixtureScript(fs, null); + nextTransaction(); + + // when + wrap(simpleObjects).create("Faz"); + + // then + final List all = wrap(simpleObjects).listAll(); + assertThat(all).hasSize(1); + } + + @Test + public void whenAlreadyExists() throws Exception { + + // given + FixtureScript fs = new SimpleObjectsTearDown(); + fixtureScripts.runFixtureScript(fs, null); + nextTransaction(); + wrap(simpleObjects).create("Faz"); + nextTransaction(); + + // then + expectedExceptions.expectCause(causalChainContains(SQLIntegrityConstraintViolationException.class)); + + // when + wrap(simpleObjects).create("Faz"); + nextTransaction(); + } + + private static Matcher causalChainContains(final Class cls) { + return new TypeSafeMatcher() { + @Override + protected boolean matchesSafely(Throwable item) { + final List causalChain = Throwables.getCausalChain(item); + for (Throwable throwable : causalChain) { + if (cls.isAssignableFrom(throwable.getClass())) { + return true; + } + } + return false; + } + + @Override + public void describeTo(Description description) { + description.appendText("exception with causal chain containing " + cls.getSimpleName()); + } + }; + } + } + +} \ No newline at end of file diff --git a/naked-objects/pom.xml b/naked-objects/pom.xml new file mode 100644 index 000000000..b3e48dcb6 --- /dev/null +++ b/naked-objects/pom.xml @@ -0,0 +1,390 @@ + + + + 4.0.0 + + java-design-patterns + com.iluwatar + 1.13.0-SNAPSHOT + + + naked-objects + + pom + + + 3.0.4 + + + + 1.9.0 + + UTF-8 + UTF-8 + 2.0.0 + + + + + apache.snapshots + Apache Snapshots + https://repository.apache.org/content/repositories/snapshots/ + + false + + + + + + Cloudbees snapshots + http://repository-estatio.forge.cloudbees.com/snapshot/ + + + + false + + + + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 1.3.1 + + + + [3.2.1,) + + + [1.8.0,) + + + All plugin versions must be + defined! + true + true + + + + + + + validate-enforce + validate + + enforce + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 1.8 + 1.8 + -parameters + + + + source + compile + + + test + test-compile + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.16 + + + **/*Test.java + **/*Test$*.java + **/*Test_*.java + **/*Spec*.java + + + **/Test*.java + **/*ForTesting.java + **/*Abstract*.java + + true + true + ${project.build.directory}/surefire-reports + + + + + org.apache.maven.plugins + maven-surefire-report-plugin + 2.16 + + false + + + + test + + + + + + maven-clean-plugin + 2.5 + + + maven-resources-plugin + 2.6 + + + maven-jar-plugin + 2.4 + + + maven-install-plugin + 2.5.1 + + + maven-deploy-plugin + 2.8.1 + + + maven-site-plugin + 3.3 + + + maven-war-plugin + 2.4 + + + + org.mortbay.jetty + maven-jetty-plugin + 6.1.26 + + + + org.apache.maven.plugins + maven-shade-plugin + 2.2 + + + + org.apache.maven.plugins + maven-antrun-plugin + 1.7 + + + + run + + + + + + + org.simplericity.jettyconsole + jetty-console-maven-plugin + 1.56 + + + + + org.apache.rat + apache-rat-plugin + 0.10 + + true + true + + **/target/** + **/target-ide/** + + **/*.project + **/.classpath + **/.settings/** + **/*.launch + **/ide/eclipse/launch/** + **/ide/intellij/launch/** + src/site/resources/ide/eclipse/** + + **/rebel.xml + **/*.gitignore + **/*.log + **/*.pdn + **/*.svg + **/*.json + **/*.min.js + **/*.js + + **/translations.pot + **/translations*.po + + + + AL2 + Apache License 2.0 + + + Licensed to the Apache Software Foundation (ASF) under one + + + + JQRY + MIT + + + Dual licensed under the MIT or GPL Version 2 licenses. + + + + JMOCK + JMock + + + Copyright (c) 2000-2007, jMock.org + + + + DOCBK + DocBook 4.5 + + + Permission to copy in any form is granted for use + Permission to use, copy, modify and distribute the DocBook DTD + is hereby granted in perpetuity, provided that the above copyright + This is the catalog data file for DocBook XML V4.5. It is provided as + XML Catalog data for DocBook XML V4.5 + DocBook additional general entities V4.5 + XML EXCHANGE TABLE MODEL DECLARATION MODULE + + + + W3C + XHTML + + + Copyright (c) 1998-2002 W3C (MIT, INRIA, Keio), + + + + + + Apache License 2.0 + + + MIT + + + JMock + + + DocBook 4.5 + + + XHTML + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-surefire-report-plugin + + + + + + + + + org.apache.isis.core + isis + ${isis.version} + pom + import + + + + org.apache.isis.viewer + isis-viewer-wicket + ${isis.version} + pom + import + + + + org.assertj + assertj-core + ${assertj-core.version} + + + + + + ${project.groupId} + naked-objects-dom + 1.13.0-SNAPSHOT + + + ${project.groupId} + naked-objects-fixture + 1.13.0-SNAPSHOT + + + ${project.groupId} + naked-objects-webapp + 1.13.0-SNAPSHOT + + + + + + + + + m2e + + + m2e.version + + + + target-ide + + + + + + dom + fixture + integtests + webapp + + \ No newline at end of file diff --git a/naked-objects/webapp/ide/eclipse/launch/.gitignore b/naked-objects/webapp/ide/eclipse/launch/.gitignore new file mode 100644 index 000000000..3d9734548 --- /dev/null +++ b/naked-objects/webapp/ide/eclipse/launch/.gitignore @@ -0,0 +1,8 @@ +/SimpleApp-PROTOTYPE-jrebel.launch +/SimpleApp-PROTOTYPE-no-fixtures.launch +/SimpleApp-PROTOTYPE-with-fixtures.launch +/SimpleApp-SERVER-no-fixtures.launch +/SimpleApp-PROTOTYPE-jrebel.launch +/SimpleApp-PROTOTYPE-no-fixtures.launch +/SimpleApp-PROTOTYPE-with-fixtures.launch +/SimpleApp-SERVER-no-fixtures.launch diff --git a/naked-objects/webapp/ide/intellij/launch/README.txt b/naked-objects/webapp/ide/intellij/launch/README.txt new file mode 100644 index 000000000..d33eaceb4 --- /dev/null +++ b/naked-objects/webapp/ide/intellij/launch/README.txt @@ -0,0 +1,25 @@ +==== + The MIT License + Copyright (c) 2014 Ilkka Seppälä + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +==== + +Copy into workspace\.idea\runConfigurations directory, and adjust file paths for Maven tasks. + diff --git a/naked-objects/webapp/ide/intellij/launch/SimpleApp_PROTOTYPE.xml b/naked-objects/webapp/ide/intellij/launch/SimpleApp_PROTOTYPE.xml new file mode 100644 index 000000000..e01c95512 --- /dev/null +++ b/naked-objects/webapp/ide/intellij/launch/SimpleApp_PROTOTYPE.xml @@ -0,0 +1,52 @@ + + + + + + \ No newline at end of file diff --git a/naked-objects/webapp/ide/intellij/launch/SimpleApp__enhance_only_.xml b/naked-objects/webapp/ide/intellij/launch/SimpleApp__enhance_only_.xml new file mode 100644 index 000000000..fcd68f002 --- /dev/null +++ b/naked-objects/webapp/ide/intellij/launch/SimpleApp__enhance_only_.xml @@ -0,0 +1,46 @@ + + +s + + + \ No newline at end of file diff --git a/naked-objects/webapp/lib/.gitignore b/naked-objects/webapp/lib/.gitignore new file mode 100644 index 000000000..70eee7e4f --- /dev/null +++ b/naked-objects/webapp/lib/.gitignore @@ -0,0 +1,5 @@ +# +# explicitly ignoring Microsoft JDBC4 jar +# (cannot redistribute, licensing) +# +sqljdbc4.jar diff --git a/naked-objects/webapp/pom.xml b/naked-objects/webapp/pom.xml new file mode 100644 index 000000000..762438aca --- /dev/null +++ b/naked-objects/webapp/pom.xml @@ -0,0 +1,321 @@ + + + + 4.0.0 + + + com.iluwatar + naked-objects + 1.13.0-SNAPSHOT + + + naked-objects-webapp + + This module runs both the Wicket viewer and the Restfulobjects viewer in a single webapp configured to run using the datanucleus object store. + + war + + + .. + + + + + + org.mortbay.jetty + maven-jetty-plugin + + + + + org.simplericity.jettyconsole + jetty-console-maven-plugin + + + + createconsole + + + ${basedir}/src/main/jettyconsole/isis-banner.png + ${project.build.directory}/${project.build.finalName}-jetty-console.jar + + package + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.8 + + + validate + + maven-version + + + + + + + maven-war-plugin + + simpleapp + + + false + + + ${maven.build.timestamp} + ${agent.name} + ${user.name} + Maven ${maven.version} + ${java.version} + ${os.name} + ${project.version} + + + WEB-INF/lib/isis-core-webserver*.jar, + WEB-INF/lib/javax.servlet-api-*.jar, + WEB-INF/lib/javax.websocket-api-*.jar, + WEB-INF/lib/jetty-all-*.jar + + + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.codehaus.mojo + build-helper-maven-plugin + [1.5,) + + maven-version + + + + + + + + + + + + + + + + + + + ${project.groupId} + naked-objects-dom + + + + org.datanucleus + datanucleus-enhancer + + + + + ${project.groupId} + naked-objects-fixture + + + + org.datanucleus + datanucleus-enhancer + + + + + + + org.apache.isis.viewer + isis-viewer-wicket-impl + + + org.apache.isis.core + isis-core-viewer-restfulobjects-server + + + org.apache.isis.core + isis-core-security-shiro + + + + + + org.apache.isis.core + isis-core-runtime + + + org.apache.isis.core + isis-core-wrapper + + + org.apache.isis.core + isis-core-security + + + + + + org.apache.isis.core + isis-core-webserver + runtime + true + + + + + org.apache.geronimo.specs + geronimo-servlet_3.0_spec + + + + + org.hsqldb + hsqldb + + + + + + + + + org.lazyluke + log4jdbc-remix + + + org.slf4j + slf4j-api + + + + + + + + + self-host + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + + + + + + + + + + + + intellij + + + idea.version + + + + + org.apache.geronimo.specs + geronimo-servlet_3.0_spec + + compile + + + + + jrebel + + + target + dom.simple,org.apache.isis.objectstore.jdo.applib + warn + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/naked-objects/webapp/src/main/java/domainapp/webapp/SimpleApplication.java b/naked-objects/webapp/src/main/java/domainapp/webapp/SimpleApplication.java new file mode 100644 index 000000000..89d316d20 --- /dev/null +++ b/naked-objects/webapp/src/main/java/domainapp/webapp/SimpleApplication.java @@ -0,0 +1,158 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package domainapp.webapp; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; + +import com.google.common.base.Joiner; +import com.google.common.io.Resources; +import com.google.inject.AbstractModule; +import com.google.inject.Module; +import com.google.inject.name.Names; +import com.google.inject.util.Modules; +import com.google.inject.util.Providers; + +import org.apache.wicket.Session; +import org.apache.wicket.request.IRequestParameters; +import org.apache.wicket.request.Request; +import org.apache.wicket.request.Response; +import org.apache.wicket.request.http.WebRequest; + +import org.apache.isis.viewer.wicket.viewer.IsisWicketApplication; +import org.apache.isis.viewer.wicket.viewer.integration.wicket.AuthenticatedWebSessionForIsis; + +import de.agilecoders.wicket.core.Bootstrap; +import de.agilecoders.wicket.core.settings.IBootstrapSettings; +import de.agilecoders.wicket.themes.markup.html.bootswatch.BootswatchTheme; +import de.agilecoders.wicket.themes.markup.html.bootswatch.BootswatchThemeProvider; + + +/** + * As specified in web.xml. + * + *

+ * See: + * + *

+ * <filter>
+ *   <filter-name>wicket</filter-name>
+ *    <filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
+ *    <init-param>
+ *      <param-name>applicationClassName</param-name>
+ *      <param-value>webapp.SimpleApplication</param-value>
+ *    </init-param>
+ * </filter>
+ * 
+ * + */ +public class SimpleApplication extends IsisWicketApplication { + + private static final long serialVersionUID = 1L; + + /** + * uncomment for a (slightly hacky) way of allowing logins using query args, eg: + * + * ?user=sven&pass=pass + * + *

+ * for demos only, obvious. + */ + private static final boolean DEMO_MODE_USING_CREDENTIALS_AS_QUERYARGS = false; + + + @Override + protected void init() { + super.init(); + + IBootstrapSettings settings = Bootstrap.getSettings(); + settings.setThemeProvider(new BootswatchThemeProvider(BootswatchTheme.Flatly)); + } + + @Override + public Session newSession(final Request request, final Response response) { + if (!DEMO_MODE_USING_CREDENTIALS_AS_QUERYARGS) { + return super.newSession(request, response); + } + + // else demo mode + final AuthenticatedWebSessionForIsis s = + (AuthenticatedWebSessionForIsis) super.newSession(request, response); + IRequestParameters requestParameters = request.getRequestParameters(); + final org.apache.wicket.util.string.StringValue user = + requestParameters.getParameterValue("user"); + final org.apache.wicket.util.string.StringValue password = + requestParameters.getParameterValue("pass"); + s.signIn(user.toString(), password.toString()); + return s; + } + + @Override + public WebRequest newWebRequest(HttpServletRequest servletRequest, String filterPath) { + if (!DEMO_MODE_USING_CREDENTIALS_AS_QUERYARGS) { + return super.newWebRequest(servletRequest, filterPath); + } + + // else demo mode + try { + String uname = servletRequest.getParameter("user"); + if (uname != null) { + servletRequest.getSession().invalidate(); + } + } catch (Exception e) { + System.out.println(e); + } + return super.newWebRequest(servletRequest, filterPath); + } + + @Override + protected Module newIsisWicketModule() { + final Module isisDefaults = super.newIsisWicketModule(); + + final Module overrides = new AbstractModule() { + @Override + protected void configure() { + bind(String.class).annotatedWith(Names.named("applicationName")).toInstance("Simple App"); + bind(String.class).annotatedWith(Names.named("applicationCss")).toInstance( + "css/application.css"); + bind(String.class).annotatedWith(Names.named("applicationJs")).toInstance( + "scripts/application.js"); + bind(String.class).annotatedWith(Names.named("welcomeMessage")).toInstance( + readLines(getClass(), "welcome.html")); + bind(String.class).annotatedWith(Names.named("aboutMessage")).toInstance("Simple App"); + bind(InputStream.class).annotatedWith(Names.named("metaInfManifest")).toProvider( + Providers.of(getServletContext().getResourceAsStream("/META-INF/MANIFEST.MF"))); + } + }; + + return Modules.override(isisDefaults).with(overrides); + } + + private static String readLines(final Class contextClass, final String resourceName) { + try { + List readLines = + Resources.readLines(Resources.getResource(contextClass, resourceName), + Charset.defaultCharset()); + return Joiner.on("\n").join(readLines); + } catch (IOException e) { + return "This is a simple app"; + } + } + +} diff --git a/naked-objects/webapp/src/main/jettyconsole/isis-banner.pdn b/naked-objects/webapp/src/main/jettyconsole/isis-banner.pdn new file mode 100644 index 000000000..37543c93c Binary files /dev/null and b/naked-objects/webapp/src/main/jettyconsole/isis-banner.pdn differ diff --git a/naked-objects/webapp/src/main/jettyconsole/isis-banner.png b/naked-objects/webapp/src/main/jettyconsole/isis-banner.png new file mode 100644 index 000000000..cd9ecfe02 Binary files /dev/null and b/naked-objects/webapp/src/main/jettyconsole/isis-banner.png differ diff --git a/naked-objects/webapp/src/main/resources/domainapp/webapp/welcome.html b/naked-objects/webapp/src/main/resources/domainapp/webapp/welcome.html new file mode 100644 index 000000000..a87d67384 --- /dev/null +++ b/naked-objects/webapp/src/main/resources/domainapp/webapp/welcome.html @@ -0,0 +1,35 @@ + + +

+ Apache Isis™ is a platform to let you rapidly develop + domain-driven apps in Java. +
+
+ This app has been generated using Apache Isis' + SimpleApp archetype, + to create a purposefully minimal application that nevertheless includes fixture data, integration tests and BDD specs. +
+
+ The app itself consists of a single domain class, SimpleObject, + along with an equally simple (factory/repository) domain service, SimpleObjects. +
+
+ For more details, see the Apache Isis website. +

diff --git a/naked-objects/webapp/src/main/webapp/WEB-INF/isis.properties b/naked-objects/webapp/src/main/webapp/WEB-INF/isis.properties new file mode 100644 index 000000000..929d1775a --- /dev/null +++ b/naked-objects/webapp/src/main/webapp/WEB-INF/isis.properties @@ -0,0 +1,300 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +################################################################################# +# +# specify system components. +# +# The values correspond to the named components in the installer-registry.properties file +# in the org.apache.isis.core:isis-core-runtime JAR (in the org.apache.isis.core.runtime package) +# +# Although all configuration could reside in isis.properties, the recommendation is +# to split out into component specific files: +# +# xxx_yyy.properties files +# +# where +# * xxx is the component type, and +# * yyy is the component name. +# +# For example, viewer_wicket.properties holds configuration information specific to the Wicket viewer. +# +################################################################################# + + +# +# configure the persistor (object store) to use +# + +# JDO/DataNucleus objectstore +isis.persistor=datanucleus + + + +# +# configure authentication mechanism to use (to logon to the system) +# + +#isis.authentication=bypass +isis.authentication=shiro + + +# +# configure authorization mechanism to use +# + +#isis.authorization=bypass +isis.authorization=shiro + + + + + +################################################################################# +# +# MetaModel +# +# The metamodel typically does not require additional configuration, although +# the system components (defined above) may refine the metamodel for their needs. +# +################################################################################# + + +# +# Additional programming model facet factories, or remove standard facet factories. +# Comma separated list of fully qualified class names. +# +#isis.reflector.facets.include= +#isis.reflector.facets.exclude= + + +# +# Metamodel validation (in addition to that automatically performed by the programming model facet factories) +# Default implementation does nothing. +# +# Use a custom implementation to enforce additional constraints specific to your app/project/company. +# +#isis.reflector.validator=org.apache.isis.core.metamodel.metamodelvalidator.dflt.MetaModelValidatorDefault + + + +# +# Whether to allow deprecated annotations/method prefixes (otherwise raise metamodel validation errors). +# If not specified, default is to allow. +# +isis.reflector.validator.allowDeprecated=false + + + +# +# Implementation to use for reading dynamic layout. Default implementation reads Xxx.layout.json files from classpath. +# +#isis.reflector.layoutMetadataReaders=org.apache.isis.core.metamodel.layoutmetadata.json.LayoutMetadataReaderFromJson + + + +# +# patterns for applying CssClassFa facet (font-awesome icons), matching on action names +# +isis.reflector.facet.cssClassFa.patterns=\ + new.*:fa-plus,\ + add.*:fa-plus-square,\ + create.*:fa-plus,\ + update.*:fa-edit,\ + change.*:fa-edit,\ + remove.*:fa-minus-square,\ + move.*:fa-exchange,\ + first.*:fa-star,\ + find.*:fa-search,\ + lookup.*:fa-search,\ + clear.*:fa-remove,\ + previous.*:fa-step-backward,\ + next.*:fa-step-forward,\ + list.*:fa-list, \ + all.*:fa-list, \ + download.*:fa-download, \ + upload.*:fa-upload, \ + execute.*:fa-bolt, \ + run.*:fa-bolt, \ + calculate.*:fa-calculator, \ + verify.*:fa-check-circle, \ + refresh.*:fa-refresh, \ + install.*:fa-wrench + + +# +# patterns for applying CssClass facet (CSS styles), matching on member names +# +isis.reflector.facet.cssClass.patterns=\ + delete.*:btn-warning + + +################################################################################# +# +# Value facet defaults +# +# (see also viewer-specific config files, eg viewer_wicket.properties) +# +################################################################################# + +# as used by @Title of a date +isis.value.format.date=dd-MM-yyyy + + + +################################################################################# +# +# Application Services and fixtures +# +################################################################################# + +# +# Specify the domain services. +# +# These are the most important configuration properties in the system, as they define +# the set of the classes for Isis to instantiate as domain service singletons. +# From these domain service instances the rest of the metamodel is discovered, while the +# end-user gains access to other domain objects by invoking the actions of the domain services. +# +isis.services-installer=configuration-and-annotation +isis.services.ServicesInstallerFromAnnotation.packagePrefix=domainapp + +# additional services/overriding default (@DomainService) implementations +isis.services = + + + +# Specify the (optional) test fixtures +# +# Fixtures are used to seed the object store with an initial set of data. For the +# in-memory object store, the fixtures are installed on every run. For other +# object stores, they are used only when the object store is first initialized. +# +isis.fixtures=domainapp.fixture.scenarios.RecreateSimpleObjects + + +# +# required by EmailServiceDefault +# +#isis.service.email.sender.address=some.valid@email.address +#isis.service.email.sender.password=the.password.for-isis.notification.email.sender.address + + + +# +# whether ExceptionRecognizers should also log any recognized exceptions +# (default false; enable for diagnostics/debugging) +# +#isis.services.exceprecog.logRecognizedExceptions=true + + +# +# disable to (automatically registered) ExceptionRecognizerCompositeForJdoObjectStore service +# almost all of this service should be registered. Since all exception recognizer implementations +# are consulted in the event of an exception, it's not sufficient to override the implementation +# (in isis.services); instead this configuration property disables this particular implementation. +# +#isis.services.ExceptionRecognizerCompositeForJdoObjectStore.disable=true + + +################################################################################ +# +# Auditing, Publishing, Command +# +################################################################################ + +# +# Whether changes to objects should be audited; if not set, defaults to "none" +# - if not set or set to "none", can explicitly enable using @DomainObject(auditing=Auditing.ENABLED) +# - if set to "all", can explicitly disable using @Object(auditing=Auditing.DISABLED) +# +#isis.services.audit.objects=all|none + +# +# Whether changes to objects should be published; if not set, defaults to "none" +# - if not set or set to "none", can explicitly enable using @DomainObject(publishing=Publishing.ENABLED) +# - if set to "all", can explicitly disable using @Object(publishing=Publishing.DISABLED) +# +#isis.services.publish.objects=all|none + +# +# Whether all (or all non-query only) actions should be published; if not set, defaults to "none" +# - if not set or set to "none", can explicitly enable using @Action(publishing=Publishing.ENABLED) +# - if set to "all", can explicitly disable using @Action(publishing=Publishing.DISABLED) +# +#isis.services.publish.actions=all|none|ignoreQueryOnly + + +# +# Whether all (or all non-query only) actions should be reified as commands; if not set, defaults to "none" +# - if not set or set to "none", can explicitly enable using @Action(command=CommandReification.ENABLED) +# - if set to "all", can explicitly disable using @Action(command=CommandReification.DISABLED) +# +#isis.services.command.actions=all|none|ignoreQueryOnly + + + + + +################################################################################ +# +# Policies +# +################################################################################# + +# +# Whether editing of object properties is allowed; if not set, defaults to "true" +# - if not set or set to "true", can explicitly disable using @DomainObject(editing=Editing.DISABLED) +# - if set to "false", can explicitly enable using @DomainObject(editing=Editing.ENABLED) +# +#isis.objects.editing=true|false + + + + + +################################################################################ +# +# i18n +# +################################################################################# + +# +# force read translations, even if running in prototype mode +# +#isis.services.translation.po.mode=read + + + + + +################################################################################ +# +# Viewer defaults +# +################################################################################# + +# +# Specify viewer defaults +# +#isis.viewers.paged.standalone=30 +#isis.viewers.paged.parented=10 + + +#isis.viewers.propertyLayout.labelPosition=LEFT +#isis.viewers.parameterLayout.labelPosition=LEFT diff --git a/naked-objects/webapp/src/main/webapp/WEB-INF/logging.properties b/naked-objects/webapp/src/main/webapp/WEB-INF/logging.properties new file mode 100644 index 000000000..62fd8ea5e --- /dev/null +++ b/naked-objects/webapp/src/main/webapp/WEB-INF/logging.properties @@ -0,0 +1,187 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +# +# Isis uses log4j is used to provide system logging +# +log4j.rootCategory=INFO, Console +#log4j.rootCategory=DEBUG, Console + + +# The console appender +log4j.appender.Console=org.apache.log4j.ConsoleAppender +log4j.appender.Console.target=System.out +log4j.appender.Console.layout=org.apache.log4j.PatternLayout +log4j.appender.Console.layout.ConversionPattern=%d{ABSOLUTE} [%-20c{1} %-10t %-5p] %m%n + + +# The stderr appender +log4j.appender.Stderr=org.apache.log4j.ConsoleAppender +log4j.appender.Stderr.target=System.err +log4j.appender.Stderr.layout=org.apache.log4j.PatternLayout +log4j.appender.Stderr.layout.ConversionPattern=%d{ABSOLUTE} [%-20c{1} %-10t %-5p] %m%n + + +# other appenders +log4j.appender.File=org.apache.log4j.RollingFileAppender +log4j.appender.File.file=isis.log +log4j.appender.File.append=false +log4j.appender.File.layout=org.apache.log4j.PatternLayout +log4j.appender.File.layout.ConversionPattern=%d [%-20c{1} %-10t %-5p] %m%n + +log4j.appender.sql=org.apache.log4j.FileAppender +log4j.appender.sql.File=./logs/sql.log +log4j.appender.sql.Append=false +log4j.appender.sql.layout=org.apache.log4j.PatternLayout +log4j.appender.sql.layout.ConversionPattern=-----> %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n%n + +log4j.appender.sqltiming=org.apache.log4j.FileAppender +log4j.appender.sqltiming.File=./logs/sqltiming.log +log4j.appender.sqltiming.Append=false +log4j.appender.sqltiming.layout=org.apache.log4j.PatternLayout +log4j.appender.sqltiming.layout.ConversionPattern=-----> %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n%n + +log4j.appender.jdbc=org.apache.log4j.FileAppender +log4j.appender.jdbc.File=./logs/jdbc.log +log4j.appender.jdbc.Append=false +log4j.appender.jdbc.layout=org.apache.log4j.PatternLayout +log4j.appender.jdbc.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} %m%n + +log4j.appender.connection=org.apache.log4j.FileAppender +log4j.appender.connection.File=./logs/connection.log +log4j.appender.connection.Append=false +log4j.appender.connection.layout=org.apache.log4j.PatternLayout +log4j.appender.connection.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} %m%n + + + + +! turn on the internal log4j debugging flag so we can see what it is doing +#log4j.debug=true + + +# DataNucleus +# the first two log the DML and DDL (if set to DEBUG) +log4j.logger.DataNucleus.Datastore.Native=WARN, Console +log4j.logger.DataNucleus.Datastore.Schema=DEBUG, Console +# the remainder can probably be left to WARN +log4j.logger.DataNucleus.Persistence=WARN, Console +log4j.logger.DataNucleus.Transaction=WARN, Console +log4j.logger.DataNucleus.Connection=WARN, Console +log4j.logger.DataNucleus.Query=WARN, Console +log4j.logger.DataNucleus.Cache=WARN, Console +log4j.logger.DataNucleus.MetaData=WARN, Console +log4j.logger.DataNucleus.Datastore=WARN, Console +log4j.logger.DataNucleus.Datastore.Persist=WARN, Console +log4j.logger.DataNucleus.Datastore.Retrieve=WARN, Console +log4j.logger.DataNucleus.General=WARN, Console +log4j.logger.DataNucleus.Lifecycle=WARN, Console +log4j.logger.DataNucleus.ValueGeneration=WARN, Console +log4j.logger.DataNucleus.Enhancer=WARN, Console +log4j.logger.DataNucleus.SchemaTool=ERROR, Console +log4j.logger.DataNucleus.JDO=WARN, Console +log4j.logger.DataNucleus.JPA=ERROR, Console +log4j.logger.DataNucleus.JCA=WARN, Console +log4j.logger.DataNucleus.IDE=ERROR, Console + +log4j.additivity.DataNucleus.Datastore.Native=false +log4j.additivity.DataNucleus.Datastore.Schema=false +log4j.additivity.DataNucleus.Datastore.Persistence=false +log4j.additivity.DataNucleus.Datastore.Transaction=false +log4j.additivity.DataNucleus.Datastore.Connection=false +log4j.additivity.DataNucleus.Datastore.Query=false +log4j.additivity.DataNucleus.Datastore.Cache=false +log4j.additivity.DataNucleus.Datastore.MetaData=false +log4j.additivity.DataNucleus.Datastore.Datastore=false +log4j.additivity.DataNucleus.Datastore.Datastore.Persist=false +log4j.additivity.DataNucleus.Datastore.Datastore.Retrieve=false +log4j.additivity.DataNucleus.Datastore.General=false +log4j.additivity.DataNucleus.Datastore.Lifecycle=false +log4j.additivity.DataNucleus.Datastore.ValueGeneration=false +log4j.additivity.DataNucleus.Datastore.Enhancer=false +log4j.additivity.DataNucleus.Datastore.SchemaTool=false +log4j.additivity.DataNucleus.Datastore.JDO=false +log4j.additivity.DataNucleus.Datastore.JPA=false +log4j.additivity.DataNucleus.Datastore.JCA=false +log4j.additivity.DataNucleus.Datastore.IDE=false + + +# if using log4jdbc-remix as JDBC driver +#log4j.logger.jdbc.sqlonly=DEBUG, sql, Console +#log4j.additivity.jdbc.sqlonly=false +#log4j.logger.jdbc.resultsettable=DEBUG, jdbc, Console +#log4j.additivity.jdbc.resultsettable=false + +#log4j.logger.jdbc.audit=WARN,jdbc, Console +#log4j.additivity.jdbc.audit=false +#log4j.logger.jdbc.resultset=WARN,jdbc +#log4j.additivity.jdbc.resultset=false +#log4j.logger.jdbc.sqltiming=WARN,sqltiming +#log4j.additivity.jdbc.sqltiming=false +#log4j.logger.jdbc.connection=FATAL,connection +#log4j.additivity.jdbc.connection=false + + + +# track Isis/JDO lifecycle integration + +#log4j.logger.org.apache.isis.runtimes.dflt.objectstores.jdo.datanucleus.persistence.FrameworkSynchronizer=DEBUG, Console +#log4j.additivity.org.apache.isis.runtimes.dflt.objectstores.jdo.datanucleus.persistence.FrameworkSynchronizer=false + +#log4j.logger.org.apache.isis.objectstore.jdo.datanucleus.persistence.IsisLifecycleListener=DEBUG,Console +#log4j.additivity.org.apache.isis.objectstore.jdo.datanucleus.persistence.IsisLifecycleListener=false + + + + +# track Isis/Wicket lifecycle integration + +#log4j.logger.org.apache.isis.viewer.wicket.viewer.integration.wicket.WebRequestCycleForIsis=DEBUG, Console +#log4j.additivity.org.apache.isis.viewer.wicket.viewer.integration.wicket.WebRequestCycleForIsis=false + +#log4j.logger.org.apache.isis.viewer.wicket.viewer.integration.isis.IsisContextForWicket=INFO,Console +#log4j.additivity.org.apache.isis.viewer.wicket.viewer.integration.isis.IsisContextForWicket=false + + + + +# quieten some of the noisier classes in Isis' bootstrapping +log4j.logger.org.apache.isis.core.metamodel.specloader.specimpl.FacetedMethodsBuilder=WARN,Console +log4j.additivity.org.apache.isis.core.metamodel.specloader.specimpl.FacetedMethodsBuilder=false + +log4j.logger.org.apache.isis.core.metamodel.specloader.ServiceInitializer=WARN,Console +log4j.additivity.org.apache.isis.core.metamodel.specloader.ServiceInitializer=false + +log4j.logger.org.apache.isis.core.runtime.services.ServicesInstallerFromConfiguration=WARN,Console +log4j.additivity.org.apache.isis.core.runtime.services.ServicesInstallerFromConfiguration=false + +log4j.logger.org.apache.isis.core.commons.config.IsisConfigurationDefault=WARN,Console +log4j.additivity.org.apache.isis.core.commons.config.IsisConfigurationDefault=false + +log4j.logger.org.apache.isis.core.runtime.installers.InstallerLookupDefault=WARN,Console +log4j.additivity.org.apache.isis.core.runtime.installers.InstallerLookupDefault=false + + +# quieten Shiro +log4j.logger.org.apache.shiro.realm.AuthorizingRealm=WARN,Console +log4j.additivity.log4j.logger.org.apache.shiro.realm.AuthorizingRealm=false + + +# Application-specific logging +log4j.logger.dom.simple.SimpleObject=DEBUG, Stderr +log4j.additivity.dom.simple.SimpleObject=false \ No newline at end of file diff --git a/naked-objects/webapp/src/main/webapp/WEB-INF/persistor.properties b/naked-objects/webapp/src/main/webapp/WEB-INF/persistor.properties new file mode 100644 index 000000000..c73af73c7 --- /dev/null +++ b/naked-objects/webapp/src/main/webapp/WEB-INF/persistor.properties @@ -0,0 +1,128 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + + +################################################################################# +# +# Persistor +# +################################################################################# + + + +# generally speaking this should not be enabled +isis.persistor.disableConcurrencyChecking=false + + + + +################################################################################# +# +# JDBC configuration +# +################################################################################# + +# +# configuration file holding the JDO objectstore's JDBC configuration +# (this is a bit of a hack... just exploiting fact that Isis also loads this file) +# + + +# +# JDBC connection details +# (also update the pom.xml to reference the appropriate JDBC driver) +# + +# +# HSQLDB in-memory +# +isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionDriverName=org.hsqldb.jdbcDriver +isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionURL=jdbc:hsqldb:mem:test +isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionUserName=sa +isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionPassword= + +# +# HSQLDB in-memory (using log4jdbc-remix) +# +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionDriverName=net.sf.log4jdbc.DriverSpy +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionURL=jdbc:log4jdbc:hsqldb:mem:test +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionUserName=sa +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionPassword= + + + +# +# HSQLDB to file +# +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionDriverName=org.hsqldb.jdbcDriver +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionURL=jdbc:hsqldb:file:/tmp/isis-simple-app/hsql-db;hsqldb.write_delay=false;shutdown=true +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionUserName=sa +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionPassword= + +# +# HSQLDB to file (using log4jdbc-remix) +# +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionDriverName=net.sf.log4jdbc.DriverSpy +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionURL=jdbc:log4jdbc:hsqldb:file:/tmp/isis-simple-app/hsql-db;hsqldb.write_delay=false;shutdown=true +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionUserName=sa +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionPassword= + + + +# +# PostgreSQL Server +# +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionDriverName=org.postgresql.Driver +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionURL=jdbc:postgresql://localhost:5432/isis +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionUserName=isis +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionPassword=isis + +# +# PostgreSQL Server (using log4jdbc-remix) +# +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionDriverName=net.sf.log4jdbc.DriverSpy +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionURL=jdbc:log4jdbc:postgresql://localhost:5432/isis +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionUserName=isis +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionPassword=isis + + + +# +# MS SQL Server +# +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionDriverName=com.microsoft.sqlserver.jdbc.SQLServerDriver +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionURL=jdbc:sqlserver://127.0.0.1:1433;instance=.;databaseName=simple +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionUserName=sa +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionPassword=p4ssword + +# +# MS SQL Server (using log4jdbc-remix) +# +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionDriverName=net.sf.log4jdbc.DriverSpy +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionURL=jdbc:log4jdbc:sqlserver://127.0.0.1:1433;instance=SQLEXPRESS;databaseName=jdo +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionUserName=jdo +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionPassword=jdopass + + + +# +# neo4j +# (experimental; run with -P neo4j profile in webapp project) +# +#isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionURL=neo4j:neo4j_DB + diff --git a/naked-objects/webapp/src/main/webapp/WEB-INF/persistor_datanucleus.properties b/naked-objects/webapp/src/main/webapp/WEB-INF/persistor_datanucleus.properties new file mode 100644 index 000000000..675ced3bf --- /dev/null +++ b/naked-objects/webapp/src/main/webapp/WEB-INF/persistor_datanucleus.properties @@ -0,0 +1,93 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# +# configuration file for the JDO/DataNucleus objectstore +# + +# identifies @PersistenceCapable entities to be eagerly registered +# if move class to other package (eg com.mycompany.myapp.dom) then update +isis.persistor.datanucleus.RegisterEntities.packagePrefix=domainapp.dom.modules + +# +# hook to perform additional initialization when JDO class metadata is loaded +# default implementation will attempt to run 'create schema' for the specified schema. +# +#isis.persistor.datanucleus.classMetadataLoadedListener=org.apache.isis.objectstore.jdo.datanucleus.CreateSchemaFromClassMetadata + + +# whether to persist the event data as a "clob" or as a "zipped" byte[] +# default is "zipped" +#isis.persistor.datanucleus.PublishingService.serializedForm=zipped + + +##################################################################### +# +# DataNucleus' configuration +# +# The 'isis.persistor.datanucleus.impl' prefix is stripped off, +# remainder is passed through to DataNucleus +# +##################################################################### + +isis.persistor.datanucleus.impl.datanucleus.schema.autoCreateAll=true +isis.persistor.datanucleus.impl.datanucleus.schema.validateTables=true +isis.persistor.datanucleus.impl.datanucleus.schema.validateConstraints=true + + +# +# Require explicit persistence (since entities are Comparable and using ObjectContracts#compareTo). +# see http://www.datanucleus.org/products/accessplatform_3_0/jdo/transaction_types.html +# +isis.persistor.datanucleus.impl.datanucleus.persistenceByReachabilityAtCommit=false + + +# +# How column names are identified +# (http://www.datanucleus.org/products/datanucleus/jdo/orm/datastore_identifiers.html) +# +isis.persistor.datanucleus.impl.datanucleus.identifier.case=MixedCase + +# +# L2 cache +# off except if explicitly marked as cacheable +# http://www.datanucleus.org/products/datanucleus/jdo/cache.html +# +isis.persistor.datanucleus.impl.datanucleus.cache.level2.type=none +isis.persistor.datanucleus.impl.datanucleus.cache.level2.mode=ENABLE_SELECTIVE + + + +# +# uncomment to use JNDI rather than direct JDBC +# +#isis.persistor.datanucleus.impl.datanucleus.ConnectionFactoryName=java:comp/env/jdbc/quickstart + +# +# uncomment to use JTA resource +# +#isis.persistor.datanucleus.impl.datanucleus.ConnectionFactory2Name=java:comp/env/jdbc/quickstart-nontx +#isis.persistor.datanucleus.impl.javax.jdo.option.TransactionType=JTA + + + +# +# +# JDBC connection details +# ... are in persistor.properties +# +# diff --git a/naked-objects/webapp/src/main/webapp/WEB-INF/shiro.ini b/naked-objects/webapp/src/main/webapp/WEB-INF/shiro.ini new file mode 100644 index 000000000..971ae697f --- /dev/null +++ b/naked-objects/webapp/src/main/webapp/WEB-INF/shiro.ini @@ -0,0 +1,93 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +[main] + +contextFactory = org.apache.isis.security.shiro.IsisLdapContextFactory +contextFactory.url = ldap://localhost:10389 +contextFactory.authenticationMechanism = CRAM-MD5 +contextFactory.systemAuthenticationMechanism = simple +contextFactory.systemUsername = uid=admin,ou=system +contextFactory.systemPassword = secret + +ldapRealm = org.apache.isis.security.shiro.IsisLdapRealm +ldapRealm.contextFactory = $contextFactory + +ldapRealm.searchBase = ou=groups,o=mojo +ldapRealm.groupObjectClass = groupOfUniqueNames +ldapRealm.uniqueMemberAttribute = uniqueMember +ldapRealm.uniqueMemberAttributeValueTemplate = uid={0} + +# optional mapping from physical groups to logical application roles +#ldapRealm.rolesByGroup = \ +# LDN_USERS: user_role,\ +# NYK_USERS: user_role,\ +# HKG_USERS: user_role,\ +# GLOBAL_ADMIN: admin_role,\ +# DEMOS: self-install_role + +ldapRealm.permissionsByRole=\ + user_role = *:ToDoItemsJdo:*:*,\ + *:ToDoItem:*:*; \ + self-install_role = *:ToDoItemsFixturesService:install:* ; \ + admin_role = * + +# to use ldap... +# (see docs for details of how to setup users/groups in Apache Directory Studio). +#securityManager.realms = $ldapRealm + +# to use .ini file +securityManager.realms = $iniRealm + + + +# ----------------------------------------------------------------------------- +# Users and their assigned roles +# +# Each line conforms to the format defined in the +# org.apache.shiro.realm.text.TextConfigurationRealm#setUserDefinitions JavaDoc +# ----------------------------------------------------------------------------- + +[users] +# user = password, role1, role2, role3, ... + + +sven = pass, admin_role +dick = pass, user_role, self-install_role +bob = pass, user_role, self-install_role +joe = pass, user_role, self-install_role +guest = guest, user_role + + + +# ----------------------------------------------------------------------------- +# Roles with assigned permissions +# +# Each line conforms to the format defined in the +# org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc +# ----------------------------------------------------------------------------- + +[roles] +# role = perm1, perm2, perm3, ... +# perm in format: packageName:className:memberName:r,w + +user_role = *:SimpleObjects:*:*,\ + *:SimpleObject:*:* +self-install_role = *:DomainAppFixtureService:*:* +admin_role = * diff --git a/naked-objects/webapp/src/main/webapp/WEB-INF/translations-en.po b/naked-objects/webapp/src/main/webapp/WEB-INF/translations-en.po new file mode 100644 index 000000000..47b82d11e --- /dev/null +++ b/naked-objects/webapp/src/main/webapp/WEB-INF/translations-en.po @@ -0,0 +1,213 @@ +############################################################################## +# +# .pot file +# +# Translate this file to each required language and place in WEB-INF, eg: +# +# /WEB-INF/translations-en_US.po +# /WEB-INF/translations-en.po +# /WEB-INF/translations-fr_FR.po +# /WEB-INF/translations-fr.po +# /WEB-INF/translations.po +# +# If the app uses TranslatableString (eg for internationalized validation +# messages), or if the app calls the TranslationService directly, then ensure +# that all text to be translated has been captured by running a full +# integration test suite that exercises all relevant behaviour +# +############################################################################## + +#: org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu#downloadTranslations(java.lang.String) +msgid ".pot file name" +msgstr ".pot file name" + + +#: org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu#resetTranslationCache() +msgid "Clear translation cache" +msgstr "Clear translation cache" + + +#: domainapp.dom.modules.simple.SimpleObjects#create() +msgid "Create" +msgstr "Create" + + +#: org.apache.isis.applib.fixturescripts.FixtureScript#discoverable +msgid "Discoverable" +msgstr "Discoverable" + + +#: org.apache.isis.applib.fixtures.FixtureType#DOMAIN_OBJECTS +msgid "Domain Objects" +msgstr "Domain Objects" + + +#: org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu#downloadTranslations() +msgid "Download Translations" +msgstr "Download Translations" + + +#: domainapp.dom.modules.simple.SimpleObject#updateName() +msgid "Exclamation mark is not allowed" +msgstr "Exclamation mark is not allowed" + + +#: domainapp.dom.modules.simple.SimpleObjects#findByName() +msgid "Find By Name" +msgstr "Find By Name" + + +#: org.apache.isis.applib.fixturescripts.FixtureResult#fixtureScriptClassName +msgid "Fixture script" +msgstr "Fixture script" + + +#: org.apache.isis.applib.fixturescripts.FixtureScript#friendlyName +msgid "Friendly Name" +msgstr "Friendly Name" + + +#: domainapp.dom.modules.simple.SimpleObject +msgid "General" +msgstr "General" + + +#: domainapp.dom.app.homepage.HomePageService#homePage() +msgid "Home Page" +msgstr "Home Page" + + +#: domainapp.dom.modules.simple.SimpleObjects#listAll() +msgid "List All" +msgstr "List All" + + +#: org.apache.isis.applib.fixturescripts.FixtureScript#localName +msgid "Local Name" +msgstr "Local Name" + + +#: org.apache.isis.applib.fixturescripts.FixtureScript#lookup() +#: org.apache.isis.applib.services.bookmark.BookmarkHolderActionContributions#lookup() +msgid "Lookup" +msgstr "Lookup" + + +#: domainapp.dom.modules.simple.SimpleObject#name +#: domainapp.dom.modules.simple.SimpleObjects#create(java.lang.String) +#: domainapp.dom.modules.simple.SimpleObjects#findByName(java.lang.String) +msgid "Name" +msgstr "Name" + + +#: domainapp.dom.modules.simple.SimpleObject#updateName(java.lang.String) +msgid "New name" +msgstr "New name" + + +#: org.apache.isis.applib.services.bookmark.BookmarkHolderAssociationContributions#object() +msgid "Object" +msgstr "Object" + + +#: domainapp.dom.modules.simple.SimpleObject#title() +msgid "Object: {name}" +msgstr "Object: {name}" + + +#: domainapp.dom.app.homepage.HomePageViewModel#objects +msgid "Objects" +msgstr "Objects" + + +#: org.apache.isis.applib.fixtures.FixtureType#OTHER +msgid "Other" +msgstr "Other" + + +#: domainapp.fixture.DomainAppFixturesService#runFixtureScript(org.apache.isis.applib.fixturescripts.FixtureScript,java.lang.String) +#: org.apache.isis.applib.fixturescripts.FixtureScripts#runFixtureScript(org.apache.isis.applib.fixturescripts.FixtureScript,java.lang.String) +msgid "Parameters" +msgstr "Parameters" + + +#: domainapp.fixture.DomainAppFixturesService +#: org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu +msgid "Prototyping" +msgstr "Prototyping" + + +#: domainapp.fixture.DomainAppFixturesService#recreateObjectsAndReturnFirst() +msgid "Recreate Objects And Return First" +msgstr "Recreate Objects And Return First" + + +#: org.apache.isis.applib.fixturescripts.FixtureResult#object +msgid "Result" +msgstr "Result" + + +#: org.apache.isis.applib.fixturescripts.FixtureResult#className +msgid "Result class" +msgstr "Result class" + + +#: org.apache.isis.applib.fixturescripts.FixtureResult#key +msgid "Result key" +msgstr "Result key" + + +#: domainapp.fixture.DomainAppFixturesService#runFixtureScript() +#: org.apache.isis.applib.fixturescripts.FixtureScripts#runFixtureScript() +msgid "Run Fixture Script" +msgstr "Run Fixture Script" + + +#: org.apache.isis.applib.fixturescripts.FixtureScript +msgid "Script" +msgstr "Script" + + +#: domainapp.fixture.DomainAppFixturesService#runFixtureScript(org.apache.isis.applib.fixturescripts.FixtureScript,java.lang.String) +#: org.apache.isis.applib.fixturescripts.FixtureScripts#runFixtureScript(org.apache.isis.applib.fixturescripts.FixtureScript,java.lang.String) +msgid "Script-specific parameters (if any). The format depends on the script implementation (eg key=value, CSV, JSON, XML etc)" +msgstr "Script-specific parameters (if any). The format depends on the script implementation (eg key=value, CSV, JSON, XML etc)" + + +#: domainapp.dom.modules.simple.SimpleObjects#title() +msgid "Simple Objects" +msgstr "Simple Objects" + + +#: org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu#switchToReadingTranslations() +msgid "Switch To Reading Translations" +msgstr "Switch To Reading Translations" + + +#: org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu#switchToWritingTranslations() +msgid "Switch To Writing Translations" +msgstr "Switch To Writing Translations" + + +#: org.apache.isis.applib.fixturescripts.FixtureScript#type +msgid "Type" +msgstr "Type" + + +#: domainapp.dom.modules.simple.SimpleObject#updateName() +msgid "Update Name" +msgstr "Update Name" + + +#: domainapp.dom.modules.simple.SimpleObject +msgid "name" +msgstr "" + + + + + +############################################################################## +# end of .pot file +############################################################################## + diff --git a/naked-objects/webapp/src/main/webapp/WEB-INF/translations-es.po b/naked-objects/webapp/src/main/webapp/WEB-INF/translations-es.po new file mode 100644 index 000000000..8b4c2d7bd --- /dev/null +++ b/naked-objects/webapp/src/main/webapp/WEB-INF/translations-es.po @@ -0,0 +1,208 @@ +############################################################################## +# +# .pot file +# +# Translate this file to each required language and place in WEB-INF, eg: +# +# /WEB-INF/translations-en_US.po +# /WEB-INF/translations-en.po +# /WEB-INF/translations-fr_FR.po +# /WEB-INF/translations-fr.po +# /WEB-INF/translations.po +# +# If the app uses TranslatableString (eg for internationalized validation +# messages), or if the app calls the TranslationService directly, then ensure +# that all text to be translated has been captured by running a full +# integration test suite that exercises all relevant behaviour +# +############################################################################## + +#: org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu#downloadTranslations(java.lang.String) +msgid ".pot file name" +msgstr "fichero .pot" + + +#: org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu#resetTranslationCache() +msgid "Clear translation cache" +msgstr "Limpiar la caché de traducciones" + + +#: domainapp.dom.modules.simple.SimpleObjects#create() +msgid "Create" +msgstr "Crear" + + +#: org.apache.isis.applib.fixturescripts.FixtureScript#discoverable +msgid "Discoverable" +msgstr "Descubrible" + + +#: org.apache.isis.applib.fixtures.FixtureType#DOMAIN_OBJECTS +msgid "Domain Objects" +msgstr "Domain Objects" + + +#: org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu#downloadTranslations() +msgid "Download Translations" +msgstr "Descargar traducciones" + + +#: domainapp.dom.modules.simple.SimpleObject#updateName() +msgid "Exclamation mark is not allowed" +msgstr "No se admite el signo de exclamación" + + +#: domainapp.dom.modules.simple.SimpleObjects#findByName() +msgid "Find By Name" +msgstr "Buscar por Nombre" + + +#: org.apache.isis.applib.fixturescripts.FixtureResult#fixtureScriptClassName +msgid "Fixture script" +msgstr "Script de Instalación" + + +#: org.apache.isis.applib.fixturescripts.FixtureScript#friendlyName +msgid "Friendly Name" +msgstr "Nombre común" + + +#: domainapp.dom.app.homepage.HomePageService#homePage() +msgid "Home Page" +msgstr "Página de Inicio" + + +#: domainapp.dom.modules.simple.SimpleObjects#listAll() +msgid "List All" +msgstr "Listar Todos" + + +#: org.apache.isis.applib.fixturescripts.FixtureScript#localName +msgid "Local Name" +msgstr "Nombre Local" + + +#: org.apache.isis.applib.fixturescripts.FixtureScript#lookup() +#: org.apache.isis.applib.services.bookmark.BookmarkHolderActionContributions#lookup() +msgid "Lookup" +msgstr "Buscar" + + +#: domainapp.dom.modules.simple.SimpleObject#name +#: domainapp.dom.modules.simple.SimpleObjects#create(java.lang.String) +#: domainapp.dom.modules.simple.SimpleObjects#findByName(java.lang.String) +msgid "Name" +msgstr "Nombre" + + +#: domainapp.dom.modules.simple.SimpleObject#updateName(java.lang.String) +msgid "New name" +msgstr "Nuevo nombre" + + +#: org.apache.isis.applib.services.bookmark.BookmarkHolderAssociationContributions#object() +msgid "Object" +msgstr "Objeto" + + +#: domainapp.dom.modules.simple.SimpleObject#title() +msgid "Object: {name}" +msgstr "Objeto: {name}" + + +#: domainapp.dom.app.homepage.HomePageViewModel#objects +msgid "Objects" +msgstr "Objetos" + + +#: org.apache.isis.applib.fixtures.FixtureType#OTHER +msgid "Other" +msgstr "Other" + + +#: domainapp.fixture.DomainAppFixturesService#runFixtureScript(org.apache.isis.applib.fixturescripts.FixtureScript,java.lang.String) +#: org.apache.isis.applib.fixturescripts.FixtureScripts#runFixtureScript(org.apache.isis.applib.fixturescripts.FixtureScript,java.lang.String) +msgid "Parameters" +msgstr "Parámetros" + + +#: domainapp.fixture.DomainAppFixturesService +#: org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu +msgid "Prototyping" +msgstr "Prototipo" + + +#: domainapp.fixture.DomainAppFixturesService#recreateObjectsAndReturnFirst() +msgid "Recreate Objects And Return First" +msgstr "Recrear Objetos y Devolver el Primero" + + +#: org.apache.isis.applib.fixturescripts.FixtureResult#object +msgid "Result" +msgstr "Resultado" + + +#: org.apache.isis.applib.fixturescripts.FixtureResult#className +msgid "Result class" +msgstr "Clase del resultado" + + +#: org.apache.isis.applib.fixturescripts.FixtureResult#key +msgid "Result key" +msgstr "Clave del Resultado" + + +#: domainapp.fixture.DomainAppFixturesService#runFixtureScript() +#: org.apache.isis.applib.fixturescripts.FixtureScripts#runFixtureScript() +msgid "Run Fixture Script" +msgstr "Ejecutar Script de Instalación" + + +#: org.apache.isis.applib.fixturescripts.FixtureScript +msgid "Script" +msgstr "Script" + + +#: domainapp.fixture.DomainAppFixturesService#runFixtureScript(org.apache.isis.applib.fixturescripts.FixtureScript,java.lang.String) +#: org.apache.isis.applib.fixturescripts.FixtureScripts#runFixtureScript(org.apache.isis.applib.fixturescripts.FixtureScript,java.lang.String) +msgid "Script-specific parameters (if any). The format depends on the script implementation (eg key=value, CSV, JSON, XML etc)" +msgstr "Parámetros específicos del Script (si hay alguno). El formato depende de la implementación del script (por ejemplo, clave=valor, CSV, JSON, XML, etc.)" + + +#: domainapp.dom.modules.simple.SimpleObjects#title() +msgid "Simple Objects" +msgstr "Objetos básicos" + + +#: org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu#switchToReadingTranslations() +msgid "Switch To Reading Translations" +msgstr "Cambiar a Lectura de Traducciones" + + +#: org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu#switchToWritingTranslations() +msgid "Switch To Writing Translations" +msgstr "Cambiar a Escritura de Traducciones" + + +#: org.apache.isis.applib.fixturescripts.FixtureScript#type +msgid "Type" +msgstr "Tipo" + + +#: domainapp.dom.modules.simple.SimpleObject#updateName() +msgid "Update Name" +msgstr "Nombre de la Actualización" + + +#: domainapp.dom.modules.simple.SimpleObject +msgid "name" +msgstr "" + + + + + +############################################################################## +# end of .pot file +############################################################################## + diff --git a/naked-objects/webapp/src/main/webapp/WEB-INF/translations-nl.po b/naked-objects/webapp/src/main/webapp/WEB-INF/translations-nl.po new file mode 100644 index 000000000..4e35a228f --- /dev/null +++ b/naked-objects/webapp/src/main/webapp/WEB-INF/translations-nl.po @@ -0,0 +1,208 @@ +############################################################################## +# +# .pot file +# +# Translate this file to each required language and place in WEB-INF, eg: +# +# /WEB-INF/translations-en_US.po +# /WEB-INF/translations-en.po +# /WEB-INF/translations-fr_FR.po +# /WEB-INF/translations-fr.po +# /WEB-INF/translations.po +# +# If the app uses TranslatableString (eg for internationalized validation +# messages), or if the app calls the TranslationService directly, then ensure +# that all text to be translated has been captured by running a full +# integration test suite that exercises all relevant behaviour +# +############################################################################## + +#: org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu#downloadTranslations(java.lang.String) +msgid ".pot file name" +msgstr "" + + +#: org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu#resetTranslationCache() +msgid "Clear translation cache" +msgstr "" + + +#: domainapp.dom.modules.simple.SimpleObjects#create() +msgid "Create" +msgstr "Creëren" + + +#: org.apache.isis.applib.fixturescripts.FixtureScript#discoverable +msgid "Discoverable" +msgstr "Discoverable" + + +#: org.apache.isis.applib.fixtures.FixtureType#DOMAIN_OBJECTS +msgid "Domain Objects" +msgstr "Domain Objects" + + +#: org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu#downloadTranslations() +msgid "Download Translations" +msgstr "" + + +#: domainapp.dom.modules.simple.SimpleObject#updateName() +msgid "Exclamation mark is not allowed" +msgstr "" + + +#: domainapp.dom.modules.simple.SimpleObjects#findByName() +msgid "Find By Name" +msgstr "Zoek op Naam" + + +#: org.apache.isis.applib.fixturescripts.FixtureResult#fixtureScriptClassName +msgid "Fixture script" +msgstr "" + + +#: org.apache.isis.applib.fixturescripts.FixtureScript#friendlyName +msgid "Friendly Name" +msgstr "" + + +#: domainapp.dom.app.homepage.HomePageService#homePage() +msgid "Home Page" +msgstr "" + + +#: domainapp.dom.modules.simple.SimpleObjects#listAll() +msgid "List All" +msgstr "Lijst Alle" + + +#: org.apache.isis.applib.fixturescripts.FixtureScript#localName +msgid "Local Name" +msgstr "" + + +#: org.apache.isis.applib.fixturescripts.FixtureScript#lookup() +#: org.apache.isis.applib.services.bookmark.BookmarkHolderActionContributions#lookup() +msgid "Lookup" +msgstr "" + + +#: domainapp.dom.modules.simple.SimpleObject#name +#: domainapp.dom.modules.simple.SimpleObjects#create(java.lang.String) +#: domainapp.dom.modules.simple.SimpleObjects#findByName(java.lang.String) +msgid "Name" +msgstr "Naam" + + +#: domainapp.dom.modules.simple.SimpleObject#updateName(java.lang.String) +msgid "New name" +msgstr "Nieuwe naam" + + +#: org.apache.isis.applib.services.bookmark.BookmarkHolderAssociationContributions#object() +msgid "Object" +msgstr "" + + +#: domainapp.dom.modules.simple.SimpleObject#title() +msgid "Object: {name}" +msgstr "" + + +#: domainapp.dom.app.homepage.HomePageViewModel#objects +msgid "Objects" +msgstr "Objects" + + +#: org.apache.isis.applib.fixtures.FixtureType#OTHER +msgid "Other" +msgstr "Other" + + +#: domainapp.fixture.DomainAppFixturesService#runFixtureScript(org.apache.isis.applib.fixturescripts.FixtureScript,java.lang.String) +#: org.apache.isis.applib.fixturescripts.FixtureScripts#runFixtureScript(org.apache.isis.applib.fixturescripts.FixtureScript,java.lang.String) +msgid "Parameters" +msgstr "" + + +#: domainapp.fixture.DomainAppFixturesService +#: org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu +msgid "Prototyping" +msgstr "" + + +#: domainapp.fixture.DomainAppFixturesService#recreateObjectsAndReturnFirst() +msgid "Recreate Objects And Return First" +msgstr "" + + +#: org.apache.isis.applib.fixturescripts.FixtureResult#object +msgid "Result" +msgstr "" + + +#: org.apache.isis.applib.fixturescripts.FixtureResult#className +msgid "Result class" +msgstr "" + + +#: org.apache.isis.applib.fixturescripts.FixtureResult#key +msgid "Result key" +msgstr "" + + +#: domainapp.fixture.DomainAppFixturesService#runFixtureScript() +#: org.apache.isis.applib.fixturescripts.FixtureScripts#runFixtureScript() +msgid "Run Fixture Script" +msgstr "" + + +#: org.apache.isis.applib.fixturescripts.FixtureScript +msgid "Script" +msgstr "" + + +#: domainapp.fixture.DomainAppFixturesService#runFixtureScript(org.apache.isis.applib.fixturescripts.FixtureScript,java.lang.String) +#: org.apache.isis.applib.fixturescripts.FixtureScripts#runFixtureScript(org.apache.isis.applib.fixturescripts.FixtureScript,java.lang.String) +msgid "Script-specific parameters (if any). The format depends on the script implementation (eg key=value, CSV, JSON, XML etc)" +msgstr "" + + +#: domainapp.dom.modules.simple.SimpleObjects#title() +msgid "Simple Objects" +msgstr "Eenvoudige Objecten" + + +#: org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu#switchToReadingTranslations() +msgid "Switch To Reading Translations" +msgstr "" + + +#: org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu#switchToWritingTranslations() +msgid "Switch To Writing Translations" +msgstr "" + + +#: org.apache.isis.applib.fixturescripts.FixtureScript#type +msgid "Type" +msgstr "" + + +#: domainapp.dom.modules.simple.SimpleObject#updateName() +msgid "Update Name" +msgstr "Updaten Naam" + + +#: domainapp.dom.modules.simple.SimpleObject +msgid "name" +msgstr "" + + + + + +############################################################################## +# end of .pot file +############################################################################## + diff --git a/naked-objects/webapp/src/main/webapp/WEB-INF/translations.po b/naked-objects/webapp/src/main/webapp/WEB-INF/translations.po new file mode 100644 index 000000000..3644a2880 --- /dev/null +++ b/naked-objects/webapp/src/main/webapp/WEB-INF/translations.po @@ -0,0 +1,213 @@ +############################################################################## +# +# .pot file +# +# Translate this file to each required language and place in WEB-INF, eg: +# +# /WEB-INF/translations-en_US.po +# /WEB-INF/translations-en.po +# /WEB-INF/translations-fr_FR.po +# /WEB-INF/translations-fr.po +# /WEB-INF/translations.po +# +# If the app uses TranslatableString (eg for internationalized validation +# messages), or if the app calls the TranslationService directly, then ensure +# that all text to be translated has been captured by running a full +# integration test suite that exercises all relevant behaviour +# +############################################################################## + +#: org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu#downloadTranslations(java.lang.String) +msgid ".pot file name" +msgstr "" + + +#: org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu#resetTranslationCache() +msgid "Clear translation cache" +msgstr "" + + +#: domainapp.dom.modules.simple.SimpleObjects#create() +msgid "Create" +msgstr "" + + +#: org.apache.isis.applib.fixturescripts.FixtureScript#discoverable +msgid "Discoverable" +msgstr "" + + +#: org.apache.isis.applib.fixtures.FixtureType#DOMAIN_OBJECTS +msgid "Domain Objects" +msgstr "" + + +#: org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu#downloadTranslations() +msgid "Download Translations" +msgstr "" + + +#: domainapp.dom.modules.simple.SimpleObject#updateName() +msgid "Exclamation mark is not allowed" +msgstr "" + + +#: domainapp.dom.modules.simple.SimpleObjects#findByName() +msgid "Find By Name" +msgstr "" + + +#: org.apache.isis.applib.fixturescripts.FixtureResult#fixtureScriptClassName +msgid "Fixture script" +msgstr "" + + +#: org.apache.isis.applib.fixturescripts.FixtureScript#friendlyName +msgid "Friendly Name" +msgstr "" + + +#: domainapp.dom.modules.simple.SimpleObject +msgid "General" +msgstr "Common" + + +#: domainapp.dom.app.homepage.HomePageService#homePage() +msgid "Home Page" +msgstr "" + + +#: domainapp.dom.modules.simple.SimpleObjects#listAll() +msgid "List All" +msgstr "" + + +#: org.apache.isis.applib.fixturescripts.FixtureScript#localName +msgid "Local Name" +msgstr "" + + +#: org.apache.isis.applib.fixturescripts.FixtureScript#lookup() +#: org.apache.isis.applib.services.bookmark.BookmarkHolderActionContributions#lookup() +msgid "Lookup" +msgstr "" + + +#: domainapp.dom.modules.simple.SimpleObject#name +#: domainapp.dom.modules.simple.SimpleObjects#create(java.lang.String) +#: domainapp.dom.modules.simple.SimpleObjects#findByName(java.lang.String) +msgid "Name" +msgstr "" + + +#: domainapp.dom.modules.simple.SimpleObject#updateName(java.lang.String) +msgid "New name" +msgstr "" + + +#: org.apache.isis.applib.services.bookmark.BookmarkHolderAssociationContributions#object() +msgid "Object" +msgstr "" + + +#: domainapp.dom.modules.simple.SimpleObject#title() +msgid "Object: {name}" +msgstr "" + + +#: domainapp.dom.app.homepage.HomePageViewModel#objects +msgid "Objects" +msgstr "" + + +#: org.apache.isis.applib.fixtures.FixtureType#OTHER +msgid "Other" +msgstr "" + + +#: domainapp.fixture.DomainAppFixturesService#runFixtureScript(org.apache.isis.applib.fixturescripts.FixtureScript,java.lang.String) +#: org.apache.isis.applib.fixturescripts.FixtureScripts#runFixtureScript(org.apache.isis.applib.fixturescripts.FixtureScript,java.lang.String) +msgid "Parameters" +msgstr "" + + +#: domainapp.fixture.DomainAppFixturesService +#: org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu +msgid "Prototyping" +msgstr "" + + +#: domainapp.fixture.DomainAppFixturesService#recreateObjectsAndReturnFirst() +msgid "Recreate Objects And Return First" +msgstr "" + + +#: org.apache.isis.applib.fixturescripts.FixtureResult#object +msgid "Result" +msgstr "" + + +#: org.apache.isis.applib.fixturescripts.FixtureResult#className +msgid "Result class" +msgstr "" + + +#: org.apache.isis.applib.fixturescripts.FixtureResult#key +msgid "Result key" +msgstr "" + + +#: domainapp.fixture.DomainAppFixturesService#runFixtureScript() +#: org.apache.isis.applib.fixturescripts.FixtureScripts#runFixtureScript() +msgid "Run Fixture Script" +msgstr "" + + +#: org.apache.isis.applib.fixturescripts.FixtureScript +msgid "Script" +msgstr "" + + +#: domainapp.fixture.DomainAppFixturesService#runFixtureScript(org.apache.isis.applib.fixturescripts.FixtureScript,java.lang.String) +#: org.apache.isis.applib.fixturescripts.FixtureScripts#runFixtureScript(org.apache.isis.applib.fixturescripts.FixtureScript,java.lang.String) +msgid "Script-specific parameters (if any). The format depends on the script implementation (eg key=value, CSV, JSON, XML etc)" +msgstr "" + + +#: domainapp.dom.modules.simple.SimpleObjects#title() +msgid "Simple Objects" +msgstr "" + + +#: org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu#switchToReadingTranslations() +msgid "Switch To Reading Translations" +msgstr "" + + +#: org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu#switchToWritingTranslations() +msgid "Switch To Writing Translations" +msgstr "" + + +#: org.apache.isis.applib.fixturescripts.FixtureScript#type +msgid "Type" +msgstr "" + + +#: domainapp.dom.modules.simple.SimpleObject#updateName() +msgid "Update Name" +msgstr "" + + +#: domainapp.dom.modules.simple.SimpleObject +msgid "name" +msgstr "" + + + + + +############################################################################## +# end of .pot file +############################################################################## + diff --git a/naked-objects/webapp/src/main/webapp/WEB-INF/viewer_restfulobjects.properties b/naked-objects/webapp/src/main/webapp/WEB-INF/viewer_restfulobjects.properties new file mode 100644 index 000000000..0a85fb681 --- /dev/null +++ b/naked-objects/webapp/src/main/webapp/WEB-INF/viewer_restfulobjects.properties @@ -0,0 +1,66 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# +# configuration file for the Restful Objects viewer +# + +# the baseUrl for hrefs in the events generated by the RO EventSerializer +isis.viewer.restfulobjects.RestfulObjectsSpecEventSerializer.baseUrl=http://localhost:8080/restful/ + +# renders param details in the (incorrect) form that they were for GSOC2013 viewers +# isis.viewer.restfulobjects.gsoc2013.legacyParamDetails=true + +# whether to honor UI hints, in particular Render(EAGERLY). Defaults to false. +#isis.viewer.restfulobjects.honorUiHints=false + + + +############################################################################### +# Non-standard configuration settings. +# +# If enabled of the following are enabled then the viewer is deviating from the +# RO spec standard; compatibility may be compromised with RO clients. +############################################################################### + +# whether to show only object properties for object members +# (on the object representation only) +# Takes precedence over the other 'suppress' below. +#isis.viewer.restfulobjects.objectPropertyValuesOnly=true + +# whether to suppress "describedby" links. Defaults to false. +#isis.viewer.restfulobjects.suppressDescribedByLinks=true + +# whether to suppress "update" links. Defaults to false. +#isis.viewer.restfulobjects.suppressUpdateLink=true + +# whether to suppress "id" json-prop for object members. Defaults to false. +#isis.viewer.restfulobjects.suppressMemberId=true + +# whether to suppress "links" json-prop for object members +# (on the object representation only). Defaults to false. +#isis.viewer.restfulobjects.suppressMemberLinks=true + +# whether to suppress "extensions" json-prop for object members +# (on the object representation only). Defaults to false. +#isis.viewer.restfulobjects.suppressMemberExtensions=true + +# whether to suppress "disabledReason" json-prop for object members +# (on the object representation only). Defaults to false. +#isis.viewer.restfulobjects.suppressMemberDisabledReason=true + +############################################################################### diff --git a/naked-objects/webapp/src/main/webapp/WEB-INF/viewer_wicket.properties b/naked-objects/webapp/src/main/webapp/WEB-INF/viewer_wicket.properties new file mode 100644 index 000000000..ba9eaaffb --- /dev/null +++ b/naked-objects/webapp/src/main/webapp/WEB-INF/viewer_wicket.properties @@ -0,0 +1,91 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# +# configuration file for the Wicket viewer +# + +# +# The maximum length of titles to display in standalone or parented tables. +# Titles longer than this length will be truncated with trailing ellipses (...) +# +# For example, if set to 12, the title +# "Buy milk on 15-Feb-13" will be truncated to "Buy milk ..." +# +# If set to 0, then only the icon will be shown. +# +isis.viewer.wicket.maxTitleLengthInStandaloneTables=0 +isis.viewer.wicket.maxTitleLengthInParentedTables=0 + + +#isis.viewer.wicket.datePattern=dd-MM-yyyy +#isis.viewer.wicket.dateTimePattern=dd-MM-yyyy HH:mm +#isis.viewer.wicket.datePickerPattern=DD-MM-YYYY + +#isis.viewer.wicket.datePattern=dd/MM/yy +#isis.viewer.wicket.dateTimePattern=dd/MM/yy HH:mm +#isis.viewer.wicket.datePickerPattern=DD/MM/YY + + +# +# whether to strip wicket tags from markup (default is true, as they may break some CSS rules) +# +#isis.viewer.wicket.stripWicketTags=false + + +# +# whether to suppress the 'rememberMe' checkbox on the login page (default is false) +# +#isis.viewer.wicket.suppressRememberMe=false + +# +# if user attempts to access a protected URL before signing in, then as a convenience the viewer will continue +# through to that destination after successful login. If you consider this to be a security risk then this flag +# disables that behaviour (default is false). +# +#isis.viewer.wicket.clearOriginalDestination=true + + +# +# whether to show action dialogs on their own page rather than as a modal dialog (default is false) +# +#isis.viewer.wicket.disableModalDialogs=false + + +# +# the maximum number of pages to list in bookmark (default is 15) +# +#isis.viewer.wicket.bookmarkedPages.maxSize=15 + + +# +# whether to show the bootstrap theme chooser (defaults false) +# +#isis.viewer.wicket.themes.showChooser=false +isis.viewer.wicket.themes.showChooser=true + +# +# comma-separated list of themes to choose from (default is to show all themes from bootswatch.com). +# +#isis.viewer.wicket.themes.enabled=bootstrap-theme,Cosmo,Flatly,Darkly,Sandstone,United + + + +# +# whether to automatically select dependent choice when the choice it depends upon changes. +# +#isis.viewer.wicket.disableDependentChoiceAutoSelection=false \ No newline at end of file diff --git a/naked-objects/webapp/src/main/webapp/WEB-INF/web.xml b/naked-objects/webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..bb6098f0b --- /dev/null +++ b/naked-objects/webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,309 @@ + + + + + Simple app + + + about/index.html + + + + + org.apache.shiro.web.env.EnvironmentLoaderListener + + + + ShiroFilter + org.apache.shiro.web.servlet.ShiroFilter + + + + ShiroFilter + /* + + + + + + + + + + + isis.viewers + wicket,restfulobjects + + + + + + + IsisLogOnExceptionFilter + org.apache.isis.core.webapp.diagnostics.IsisLogOnExceptionFilter + + + IsisLogOnExceptionFilter + /wicket/* + + + IsisLogOnExceptionFilter + /restful/* + + + + + + + ResourceCachingFilter + org.apache.isis.core.webapp.content.ResourceCachingFilter + + CacheTime + 86400 + + + + ResourceCachingFilter + *.js + + + ResourceCachingFilter + *.css + + + ResourceCachingFilter + *.png + + + ResourceCachingFilter + *.jpg + + + ResourceCachingFilter + *.gif + + + ResourceCachingFilter + *.html + + + ResourceCachingFilter + *.swf + + + + Resource + org.apache.isis.core.webapp.content.ResourceServlet + + + Resource + *.css + + + Resource + *.png + + + Resource + *.jpg + + + Resource + *.gif + + + Resource + *.js + + + Resource + *.html + + + Resource + *.swf + + + + + + + WicketFilter + org.apache.wicket.protocol.http.WicketFilter + + applicationClassName + domainapp.webapp.SimpleApplication + + + + WicketFilter + /wicket/* + + + + + configuration + + development + + + + + + + + + + org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap + + + + + javax.ws.rs.Application + org.apache.isis.viewer.restfulobjects.server.RestfulObjectsApplication + + + + resteasy.servlet.mapping.prefix + /restful/ + + + + + + IsisSessionFilterForRestfulObjects + org.apache.isis.core.webapp.IsisSessionFilter + + + authenticationSessionStrategy + org.apache.isis.viewer.restfulobjects.server.authentication.AuthenticationSessionStrategyBasicAuth + + + + whenNoSession + basicAuthChallenge + + + + + IsisSessionFilterForRestfulObjects + RestfulObjectsRestEasyDispatcher + + + + IsisTransactionFilterForRestfulObjects + org.apache.isis.viewer.restfulobjects.server.webapp.IsisTransactionFilterForRestfulObjects + + + IsisTransactionFilterForRestfulObjects + RestfulObjectsRestEasyDispatcher + + + + + RestfulObjectsRestEasyDispatcher + org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher + + + RestfulObjectsRestEasyDispatcher + /restful/* + + + + + + + + + + diff --git a/naked-objects/webapp/src/main/webapp/about/images/isis-logo.png b/naked-objects/webapp/src/main/webapp/about/images/isis-logo.png new file mode 100644 index 000000000..5284fe732 Binary files /dev/null and b/naked-objects/webapp/src/main/webapp/about/images/isis-logo.png differ diff --git a/naked-objects/webapp/src/main/webapp/about/index.html b/naked-objects/webapp/src/main/webapp/about/index.html new file mode 100644 index 000000000..bfcc52017 --- /dev/null +++ b/naked-objects/webapp/src/main/webapp/about/index.html @@ -0,0 +1,126 @@ + + + + + + + Apache Isis™ SimpleApp + + + + +
+ Isis Logo + +

+ This app has been generated using Apache Isis' + SimpleApp archetype, + to create a purposefully minimal application that nevertheless includes fixture data, integration tests and BDD specs. +
+
+ The app itself consists of a single domain class, SimpleObject, + along with an equally simple (factory/repository) domain service, SimpleObjects. +

+ +

To access the app:

+
    +
  • +

    + wicket/ +

    +

    + provides accesses to a generic UI for end-users, + Isis' Wicket Viewer. + As its name suggests, this viewer is built on top of Apache Wicket™. +

    +
  • +
  • +

    + + restful/ + +

    +

    + provides access to a RESTful API conformant with the + Restful Objects spec. This is part of Apache Isis Core. The + implementation technology is JBoss RestEasy. +

    +
  • +
+ +

+ The default user/password is sven/pass (as configured in the + shiro.ini file). +

+ +
+ + diff --git a/naked-objects/webapp/src/main/webapp/css/application.css b/naked-objects/webapp/src/main/webapp/css/application.css new file mode 100644 index 000000000..9f1612af1 --- /dev/null +++ b/naked-objects/webapp/src/main/webapp/css/application.css @@ -0,0 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + diff --git a/naked-objects/webapp/src/main/webapp/images/spinning-icon.gif b/naked-objects/webapp/src/main/webapp/images/spinning-icon.gif new file mode 100644 index 000000000..75e3b1e5b Binary files /dev/null and b/naked-objects/webapp/src/main/webapp/images/spinning-icon.gif differ diff --git a/naked-objects/webapp/src/main/webapp/scripts/application.js b/naked-objects/webapp/src/main/webapp/scripts/application.js new file mode 100644 index 000000000..3803b6607 --- /dev/null +++ b/naked-objects/webapp/src/main/webapp/scripts/application.js @@ -0,0 +1,25 @@ +/* + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +$(document).ready(function() { + /// here... +}); \ No newline at end of file diff --git a/null-object/README.md b/null-object/README.md new file mode 100644 index 000000000..0ed28a0af --- /dev/null +++ b/null-object/README.md @@ -0,0 +1,30 @@ +--- +layout: pattern +title: Null Object +folder: null-object +permalink: /patterns/null-object/ +categories: Behavioral +tags: + - Java + - Difficulty-Beginner +--- + +## Intent +In most object-oriented languages, such as Java or C#, references +may be null. These references need to be checked to ensure they are not null +before invoking any methods, because methods typically cannot be invoked on +null references. Instead of using a null reference to convey absence of an +object (for instance, a non-existent customer), one uses an object which +implements the expected interface, but whose method body is empty. The +advantage of this approach over a working default implementation is that a Null +Object is very predictable and has no side effects: it does nothing. + +![alt text](./etc/null-object.png "Null Object") + +## Applicability +Use the Null Object pattern when + +* you want to avoid explicit null checks and keep the algorithm elegant and easy to read. + +## Credits +* [Pattern Languages of Program Design](http://www.amazon.com/Pattern-Languages-Program-Design-Coplien/dp/0201607344/ref=sr_1_1) diff --git a/null-object/pom.xml b/null-object/pom.xml index 14d872f6c..b0adad1af 100644 --- a/null-object/pom.xml +++ b/null-object/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT null-object @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/null-object/src/main/java/com/iluwatar/nullobject/App.java b/null-object/src/main/java/com/iluwatar/nullobject/App.java index 35b05061e..90a9c12c8 100644 --- a/null-object/src/main/java/com/iluwatar/nullobject/App.java +++ b/null-object/src/main/java/com/iluwatar/nullobject/App.java @@ -1,32 +1,50 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.nullobject; /** * - * Null Object pattern replaces null values with neutral objects. - * Many times this simplifies algorithms since no extra null checks - * are needed. - * - * In this example we build a binary tree where the nodes are either - * normal or Null Objects. No null values are used in the tree making - * the traversal easy. + * Null Object pattern replaces null values with neutral objects. Many times this simplifies + * algorithms since no extra null checks are needed. + *

+ * In this example we build a binary tree where the nodes are either normal or Null Objects. No null + * values are used in the tree making the traversal easy. * */ -public class App -{ - public static void main( String[] args ) { - - Node root = new NodeImpl("1", - new NodeImpl("11", - new NodeImpl("111", - NullNode.getInstance(), - NullNode.getInstance()), - NullNode.getInstance()), - new NodeImpl("12", - NullNode.getInstance(), - new NodeImpl("122", - NullNode.getInstance(), - NullNode.getInstance()))); +public class App { + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { - root.walk(); - } + Node root = + new NodeImpl("1", new NodeImpl("11", new NodeImpl("111", NullNode.getInstance(), + NullNode.getInstance()), NullNode.getInstance()), new NodeImpl("12", + NullNode.getInstance(), new NodeImpl("122", NullNode.getInstance(), + NullNode.getInstance()))); + + root.walk(); + } } diff --git a/null-object/src/main/java/com/iluwatar/nullobject/Node.java b/null-object/src/main/java/com/iluwatar/nullobject/Node.java index 3d52087c9..36363736c 100644 --- a/null-object/src/main/java/com/iluwatar/nullobject/Node.java +++ b/null-object/src/main/java/com/iluwatar/nullobject/Node.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.nullobject; /** @@ -7,9 +29,13 @@ package com.iluwatar.nullobject; */ public interface Node { - String getName(); - int getTreeSize(); - Node getLeft(); - Node getRight(); - void walk(); + String getName(); + + int getTreeSize(); + + Node getLeft(); + + Node getRight(); + + void walk(); } diff --git a/null-object/src/main/java/com/iluwatar/nullobject/NodeImpl.java b/null-object/src/main/java/com/iluwatar/nullobject/NodeImpl.java index fc9c9c9e3..46787ac96 100644 --- a/null-object/src/main/java/com/iluwatar/nullobject/NodeImpl.java +++ b/null-object/src/main/java/com/iluwatar/nullobject/NodeImpl.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.nullobject; /** @@ -7,44 +29,47 @@ package com.iluwatar.nullobject; */ public class NodeImpl implements Node { - private final String name; - private final Node left; - private final Node right; - - public NodeImpl(String name, Node left, Node right) { - this.name = name; - this.left = left; - this.right = right; - } - - @Override - public int getTreeSize() { - return 1 + left.getTreeSize() + right.getTreeSize(); - } + private final String name; + private final Node left; + private final Node right; - @Override - public Node getLeft() { - return left; - } + /** + * Constructor + */ + public NodeImpl(String name, Node left, Node right) { + this.name = name; + this.left = left; + this.right = right; + } - @Override - public Node getRight() { - return right; - } + @Override + public int getTreeSize() { + return 1 + left.getTreeSize() + right.getTreeSize(); + } - @Override - public String getName() { - return name; - } + @Override + public Node getLeft() { + return left; + } - @Override - public void walk() { - System.out.println(name); - if (left.getTreeSize() > 0) { - left.walk(); - } - if (right.getTreeSize() > 0) { - right.walk(); - } - } + @Override + public Node getRight() { + return right; + } + + @Override + public String getName() { + return name; + } + + @Override + public void walk() { + System.out.println(name); + if (left.getTreeSize() > 0) { + left.walk(); + } + if (right.getTreeSize() > 0) { + right.walk(); + } + } } diff --git a/null-object/src/main/java/com/iluwatar/nullobject/NullNode.java b/null-object/src/main/java/com/iluwatar/nullobject/NullNode.java index dfa6963d0..d4ea0e429 100644 --- a/null-object/src/main/java/com/iluwatar/nullobject/NullNode.java +++ b/null-object/src/main/java/com/iluwatar/nullobject/NullNode.java @@ -1,44 +1,66 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.nullobject; /** * * Null Object implementation for binary tree node. - * + *

* Implemented as Singleton, since all the NullNodes are the same. * */ -public class NullNode implements Node { +public final class NullNode implements Node { - private static NullNode instance = new NullNode(); - - private NullNode() { - } - - public static NullNode getInstance() { - return instance; - } - - @Override - public int getTreeSize() { - return 0; - } + private static NullNode instance = new NullNode(); - @Override - public Node getLeft() { - return null; - } + private NullNode() {} - @Override - public Node getRight() { - return null; - } + public static NullNode getInstance() { + return instance; + } - @Override - public String getName() { - return null; - } + @Override + public int getTreeSize() { + return 0; + } - @Override - public void walk() { - } + @Override + public Node getLeft() { + return null; + } + + @Override + public Node getRight() { + return null; + } + + @Override + public String getName() { + return null; + } + + @Override + public void walk() { + // Do nothing + } } diff --git a/null-object/src/test/java/com/iluwatar/nullobject/AppTest.java b/null-object/src/test/java/com/iluwatar/nullobject/AppTest.java index eb12f3c4d..170f7b977 100644 --- a/null-object/src/test/java/com/iluwatar/nullobject/AppTest.java +++ b/null-object/src/test/java/com/iluwatar/nullobject/AppTest.java @@ -1,14 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.nullobject; import org.junit.Test; -import com.iluwatar.nullobject.App; - +/** + * + * Application test + * + */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/null-object/src/test/java/com/iluwatar/nullobject/NullNodeTest.java b/null-object/src/test/java/com/iluwatar/nullobject/NullNodeTest.java new file mode 100644 index 000000000..1375b8949 --- /dev/null +++ b/null-object/src/test/java/com/iluwatar/nullobject/NullNodeTest.java @@ -0,0 +1,65 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.nullobject; + +import org.junit.Test; +import org.mockito.Mockito; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + +/** + * Date: 12/26/15 - 11:47 PM + * + * @author Jeroen Meulemeester + */ +public class NullNodeTest extends StdOutTest { + + /** + * Verify if {@link NullNode#getInstance()} actually returns the same object instance + */ + @Test + public void testGetInstance() { + final NullNode instance = NullNode.getInstance(); + assertNotNull(instance); + assertSame(instance, NullNode.getInstance()); + } + + @Test + public void testFields() { + final NullNode node = NullNode.getInstance(); + assertEquals(0, node.getTreeSize()); + assertNull(node.getName()); + assertNull(node.getLeft()); + assertNull(node.getRight()); + } + + @Test + public void testWalk() throws Exception { + NullNode.getInstance().walk(); + Mockito.verifyZeroInteractions(getStdOutMock()); + } + +} \ No newline at end of file diff --git a/null-object/src/test/java/com/iluwatar/nullobject/StdOutTest.java b/null-object/src/test/java/com/iluwatar/nullobject/StdOutTest.java new file mode 100644 index 000000000..0c0122132 --- /dev/null +++ b/null-object/src/test/java/com/iluwatar/nullobject/StdOutTest.java @@ -0,0 +1,76 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.nullobject; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since walking through the tree has no + * influence on any other accessible object, except for writing to std-out using {@link + * System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/null-object/src/test/java/com/iluwatar/nullobject/TreeTest.java b/null-object/src/test/java/com/iluwatar/nullobject/TreeTest.java new file mode 100644 index 000000000..6c77cd236 --- /dev/null +++ b/null-object/src/test/java/com/iluwatar/nullobject/TreeTest.java @@ -0,0 +1,123 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.nullobject; + +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mockito; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; + +/** + * Date: 12/26/15 - 11:44 PM + * + * @author Jeroen Meulemeester + */ +public class TreeTest extends StdOutTest { + + /** + * During the tests, the same tree structure will be used, shown below. End points will be + * terminated with the {@link NullNode} instance. + * + *

+   * root
+   * ├── level1_a
+   * │   ├── level2_a
+   * │   │   ├── level3_a
+   * │   │   └── level3_b
+   * │   └── level2_b
+   * └── level1_b
+   * 
+ */ + private static final Node TREE_ROOT; + + static { + final NodeImpl level1B = new NodeImpl("level1_b", NullNode.getInstance(), NullNode.getInstance()); + final NodeImpl level2B = new NodeImpl("level2_b", NullNode.getInstance(), NullNode.getInstance()); + final NodeImpl level3A = new NodeImpl("level3_a", NullNode.getInstance(), NullNode.getInstance()); + final NodeImpl level3B = new NodeImpl("level3_b", NullNode.getInstance(), NullNode.getInstance()); + final NodeImpl level2A = new NodeImpl("level2_a", level3A, level3B); + final NodeImpl level1A = new NodeImpl("level1_a", level2A, level2B); + TREE_ROOT = new NodeImpl("root", level1A, level1B); + } + + /** + * Verify the number of items in the tree. The root has 6 children so we expect a {@link + * Node#getTreeSize()} of 7 {@link Node}s in total. + */ + @Test + public void testTreeSize() { + assertEquals(7, TREE_ROOT.getTreeSize()); + } + + /** + * Walk through the tree and verify if every item is handled + */ + @Test + public void testWalk() { + TREE_ROOT.walk(); + + final InOrder inOrder = Mockito.inOrder(getStdOutMock()); + inOrder.verify(getStdOutMock()).println("root"); + inOrder.verify(getStdOutMock()).println("level1_a"); + inOrder.verify(getStdOutMock()).println("level2_a"); + inOrder.verify(getStdOutMock()).println("level3_a"); + inOrder.verify(getStdOutMock()).println("level3_b"); + inOrder.verify(getStdOutMock()).println("level2_b"); + inOrder.verify(getStdOutMock()).println("level1_b"); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testGetLeft() throws Exception { + final Node level1 = TREE_ROOT.getLeft(); + assertNotNull(level1); + assertEquals("level1_a", level1.getName()); + assertEquals(5, level1.getTreeSize()); + + final Node level2 = level1.getLeft(); + assertNotNull(level2); + assertEquals("level2_a", level2.getName()); + assertEquals(3, level2.getTreeSize()); + + final Node level3 = level2.getLeft(); + assertNotNull(level3); + assertEquals("level3_a", level3.getName()); + assertEquals(1, level3.getTreeSize()); + assertSame(NullNode.getInstance(), level3.getRight()); + assertSame(NullNode.getInstance(), level3.getLeft()); + } + + @Test + public void testGetRight() throws Exception { + final Node level1 = TREE_ROOT.getRight(); + assertNotNull(level1); + assertEquals("level1_b", level1.getName()); + assertEquals(1, level1.getTreeSize()); + assertSame(NullNode.getInstance(), level1.getRight()); + assertSame(NullNode.getInstance(), level1.getLeft()); + } + +} diff --git a/object-pool/README.md b/object-pool/README.md new file mode 100644 index 000000000..cf36d9880 --- /dev/null +++ b/object-pool/README.md @@ -0,0 +1,25 @@ +--- +layout: pattern +title: Object Pool +folder: object-pool +permalink: /patterns/object-pool/ +categories: Creational +tags: + - Java + - Difficulty-Beginner + - Performance +--- + +## Intent +When objects are expensive to create and they are needed only for +short periods of time it is advantageous to utilize the Object Pool pattern. +The Object Pool provides a cache for instantiated objects tracking which ones +are in use and which are available. + +![alt text](./etc/object-pool.png "Object Pool") + +## Applicability +Use the Object Pool pattern when + +* the objects are expensive to create (allocation cost) +* you need a large number of short-lived objects (memory fragmentation) diff --git a/object-pool/pom.xml b/object-pool/pom.xml index 59406068a..666242d1d 100644 --- a/object-pool/pom.xml +++ b/object-pool/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT object-pool diff --git a/object-pool/src/main/java/com/iluwatar/object/pool/App.java b/object-pool/src/main/java/com/iluwatar/object/pool/App.java new file mode 100644 index 000000000..66b3e3f90 --- /dev/null +++ b/object-pool/src/main/java/com/iluwatar/object/pool/App.java @@ -0,0 +1,74 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.object.pool; + +/** + * + * When it is necessary to work with a large number of objects that are particularly expensive to + * instantiate and each object is only needed for a short period of time, the performance of an + * entire application may be adversely affected. An object pool design pattern may be deemed + * desirable in cases such as these. + *

+ * The object pool design pattern creates a set of objects that may be reused. When a new object is + * needed, it is requested from the pool. If a previously prepared object is available it is + * returned immediately, avoiding the instantiation cost. If no objects are present in the pool, a + * new item is created and returned. When the object has been used and is no longer needed, it is + * returned to the pool, allowing it to be used again in the future without repeating the + * computationally expensive instantiation process. It is important to note that once an object has + * been used and returned, existing references will become invalid. + *

+ * In this example we have created {@link OliphauntPool} inheriting from generic {@link ObjectPool}. + * {@link Oliphaunt}s can be checked out from the pool and later returned to it. The pool tracks + * created instances and their status (available, inUse). + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + OliphauntPool pool = new OliphauntPool(); + System.out.println(pool); + Oliphaunt oliphaunt1 = pool.checkOut(); + System.out.println("Checked out " + oliphaunt1); + System.out.println(pool); + Oliphaunt oliphaunt2 = pool.checkOut(); + System.out.println("Checked out " + oliphaunt2); + Oliphaunt oliphaunt3 = pool.checkOut(); + System.out.println("Checked out " + oliphaunt3); + System.out.println(pool); + System.out.println("Checking in " + oliphaunt1); + pool.checkIn(oliphaunt1); + System.out.println("Checking in " + oliphaunt2); + pool.checkIn(oliphaunt2); + System.out.println(pool); + Oliphaunt oliphaunt4 = pool.checkOut(); + System.out.println("Checked out " + oliphaunt4); + Oliphaunt oliphaunt5 = pool.checkOut(); + System.out.println("Checked out " + oliphaunt5); + System.out.println(pool); + } +} diff --git a/object-pool/src/main/java/com/iluwatar/object/pool/ObjectPool.java b/object-pool/src/main/java/com/iluwatar/object/pool/ObjectPool.java new file mode 100644 index 000000000..3fcbd2c41 --- /dev/null +++ b/object-pool/src/main/java/com/iluwatar/object/pool/ObjectPool.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.object.pool; + +import java.util.HashSet; + +/** + * + * Generic object pool + */ +public abstract class ObjectPool { + + private HashSet available = new HashSet<>(); + private HashSet inUse = new HashSet<>(); + + protected abstract T create(); + + /** + * Checkout object from pool + */ + public synchronized T checkOut() { + if (available.size() <= 0) { + available.add(create()); + } + T instance = available.iterator().next(); + available.remove(instance); + inUse.add(instance); + return instance; + } + + public synchronized void checkIn(T instance) { + inUse.remove(instance); + available.add(instance); + } + + @Override + public String toString() { + return String.format("Pool available=%d inUse=%d", available.size(), inUse.size()); + } +} diff --git a/object-pool/src/main/java/com/iluwatar/object/pool/Oliphaunt.java b/object-pool/src/main/java/com/iluwatar/object/pool/Oliphaunt.java new file mode 100644 index 000000000..46c1a03c9 --- /dev/null +++ b/object-pool/src/main/java/com/iluwatar/object/pool/Oliphaunt.java @@ -0,0 +1,56 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.object.pool; + +/** + * + * Oliphaunts are expensive to create + * + */ +public class Oliphaunt { + + private static int counter = 1; + + private final int id; + + /** + * Constructor + */ + public Oliphaunt() { + id = counter++; + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + public int getId() { + return id; + } + + @Override + public String toString() { + return String.format("Oliphaunt id=%d", id); + } +} diff --git a/object-pool/src/main/java/com/iluwatar/object/pool/OliphauntPool.java b/object-pool/src/main/java/com/iluwatar/object/pool/OliphauntPool.java new file mode 100644 index 000000000..17b998a0e --- /dev/null +++ b/object-pool/src/main/java/com/iluwatar/object/pool/OliphauntPool.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.object.pool; + +/** + * + * Oliphaunt object pool + * + */ +public class OliphauntPool extends ObjectPool { + + @Override + protected Oliphaunt create() { + return new Oliphaunt(); + } +} diff --git a/object-pool/src/main/java/com/iluwatar/objectpool/App.java b/object-pool/src/main/java/com/iluwatar/objectpool/App.java deleted file mode 100644 index 2c6c855e5..000000000 --- a/object-pool/src/main/java/com/iluwatar/objectpool/App.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.iluwatar.objectpool; - -/** - * - * When it is necessary to work with a large number of objects that are particularly expensive to instantiate - * and each object is only needed for a short period of time, the performance of an entire application may be - * adversely affected. An object pool design pattern may be deemed desirable in cases such as these. - * - * The object pool design pattern creates a set of objects that may be reused. When a new object is needed, it - * is requested from the pool. If a previously prepared object is available it is returned immediately, avoiding - * the instantiation cost. If no objects are present in the pool, a new item is created and returned. When the - * object has been used and is no longer needed, it is returned to the pool, allowing it to be used again in the - * future without repeating the computationally expensive instantiation process. It is important to note that - * once an object has been used and returned, existing references will become invalid. - * - * In this example we have created OliphauntPool inheriting from generic ObjectPool. Oliphaunts can be checked - * out from the pool and later returned to it. The pool tracks created instances and their status (available, - * inUse). - * - */ -public class App { - - public static void main( String[] args ) { - OliphauntPool pool = new OliphauntPool(); - System.out.println(pool); - Oliphaunt oliphaunt1 = pool.checkOut(); - System.out.println("Checked out " + oliphaunt1); - System.out.println(pool); - Oliphaunt oliphaunt2 = pool.checkOut(); - System.out.println("Checked out " + oliphaunt2); - Oliphaunt oliphaunt3 = pool.checkOut(); - System.out.println("Checked out " + oliphaunt3); - System.out.println(pool); - System.out.println("Checking in " + oliphaunt1); - pool.checkIn(oliphaunt1); - System.out.println("Checking in " + oliphaunt2); - pool.checkIn(oliphaunt2); - System.out.println(pool); - Oliphaunt oliphaunt4 = pool.checkOut(); - System.out.println("Checked out " + oliphaunt4); - Oliphaunt oliphaunt5 = pool.checkOut(); - System.out.println("Checked out " + oliphaunt5); - System.out.println(pool); - } -} diff --git a/object-pool/src/main/java/com/iluwatar/objectpool/ObjectPool.java b/object-pool/src/main/java/com/iluwatar/objectpool/ObjectPool.java deleted file mode 100644 index 343f29aef..000000000 --- a/object-pool/src/main/java/com/iluwatar/objectpool/ObjectPool.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.iluwatar.objectpool; - -import java.util.HashSet; - -/** - * - * Generic object pool - * - * @param - */ -public abstract class ObjectPool { - - private HashSet available = new HashSet<>(); - private HashSet inUse = new HashSet<>(); - - protected abstract T create(); - - public synchronized T checkOut() { - if (available.size() <= 0) { - available.add(create()); - } - T instance = available.iterator().next(); - available.remove(instance); - inUse.add(instance); - return instance; - } - - public synchronized void checkIn(T instance) { - inUse.remove(instance); - available.add(instance); - } - - @Override - public String toString() { - return String.format("Pool available=%d inUse=%d", available.size(), inUse.size()); - } -} diff --git a/object-pool/src/main/java/com/iluwatar/objectpool/Oliphaunt.java b/object-pool/src/main/java/com/iluwatar/objectpool/Oliphaunt.java deleted file mode 100644 index 29d02d2e8..000000000 --- a/object-pool/src/main/java/com/iluwatar/objectpool/Oliphaunt.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.iluwatar.objectpool; - -/** - * - * Oliphaunts are expensive to create - * - */ -public class Oliphaunt { - - private static int counter = 1; - - private final int id; - - public Oliphaunt() { - id = counter++; - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - public int getId() { - return id; - } - - @Override - public String toString() { - return String.format("Oliphaunt id=%d", id); - } -} diff --git a/object-pool/src/main/java/com/iluwatar/objectpool/OliphauntPool.java b/object-pool/src/main/java/com/iluwatar/objectpool/OliphauntPool.java deleted file mode 100644 index 25f591f00..000000000 --- a/object-pool/src/main/java/com/iluwatar/objectpool/OliphauntPool.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.iluwatar.objectpool; - -/** - * - * Oliphaunt object pool - * - */ -public class OliphauntPool extends ObjectPool { - - @Override - protected Oliphaunt create() { - return new Oliphaunt(); - } -} diff --git a/object-pool/src/test/java/com/iluwatar/object/pool/AppTest.java b/object-pool/src/test/java/com/iluwatar/object/pool/AppTest.java new file mode 100644 index 000000000..45aa1b253 --- /dev/null +++ b/object-pool/src/test/java/com/iluwatar/object/pool/AppTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.object.pool; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/object-pool/src/test/java/com/iluwatar/object/pool/OliphauntPoolTest.java b/object-pool/src/test/java/com/iluwatar/object/pool/OliphauntPoolTest.java new file mode 100644 index 000000000..f855e18c4 --- /dev/null +++ b/object-pool/src/test/java/com/iluwatar/object/pool/OliphauntPoolTest.java @@ -0,0 +1,121 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.object.pool; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +/** + * Date: 12/27/15 - 1:05 AM + * + * @author Jeroen Meulemeester + */ +public class OliphauntPoolTest { + + /** + * Use the same object 100 times subsequently. This should not take much time since the heavy + * object instantiation is done only once. Verify if we get the same object each time. + */ + @Test(timeout = 5000) + public void testSubsequentCheckinCheckout() { + final OliphauntPool pool = new OliphauntPool(); + assertEquals(pool.toString(), "Pool available=0 inUse=0"); + + final Oliphaunt expectedOliphaunt = pool.checkOut(); + assertEquals(pool.toString(), "Pool available=0 inUse=1"); + + pool.checkIn(expectedOliphaunt); + assertEquals(pool.toString(), "Pool available=1 inUse=0"); + + for (int i = 0; i < 100; i++) { + final Oliphaunt oliphaunt = pool.checkOut(); + assertEquals(pool.toString(), "Pool available=0 inUse=1"); + assertSame(expectedOliphaunt, oliphaunt); + assertEquals(expectedOliphaunt.getId(), oliphaunt.getId()); + assertEquals(expectedOliphaunt.toString(), oliphaunt.toString()); + + pool.checkIn(oliphaunt); + assertEquals(pool.toString(), "Pool available=1 inUse=0"); + } + + } + + /** + * Use the same object 100 times subsequently. This should not take much time since the heavy + * object instantiation is done only once. Verify if we get the same object each time. + */ + @Test(timeout = 5000) + public void testConcurrentCheckinCheckout() { + final OliphauntPool pool = new OliphauntPool(); + assertEquals(pool.toString(), "Pool available=0 inUse=0"); + + final Oliphaunt firstOliphaunt = pool.checkOut(); + assertEquals(pool.toString(), "Pool available=0 inUse=1"); + + final Oliphaunt secondOliphaunt = pool.checkOut(); + assertEquals(pool.toString(), "Pool available=0 inUse=2"); + + assertNotSame(firstOliphaunt, secondOliphaunt); + assertEquals(firstOliphaunt.getId() + 1, secondOliphaunt.getId()); + + // After checking in the second, we should get the same when checking out a new oliphaunt ... + pool.checkIn(secondOliphaunt); + assertEquals(pool.toString(), "Pool available=1 inUse=1"); + + final Oliphaunt oliphaunt3 = pool.checkOut(); + assertEquals(pool.toString(), "Pool available=0 inUse=2"); + assertSame(secondOliphaunt, oliphaunt3); + + // ... and the same applies for the first one + pool.checkIn(firstOliphaunt); + assertEquals(pool.toString(), "Pool available=1 inUse=1"); + + final Oliphaunt oliphaunt4 = pool.checkOut(); + assertEquals(pool.toString(), "Pool available=0 inUse=2"); + assertSame(firstOliphaunt, oliphaunt4); + + // When both oliphaunt return to the pool, we should still get the same instances + pool.checkIn(firstOliphaunt); + assertEquals(pool.toString(), "Pool available=1 inUse=1"); + + pool.checkIn(secondOliphaunt); + assertEquals(pool.toString(), "Pool available=2 inUse=0"); + + // The order of the returned instances is not determined, so just put them in a list + // and verify if both expected instances are in there. + final List oliphaunts = Arrays.asList(pool.checkOut(), pool.checkOut()); + assertEquals(pool.toString(), "Pool available=0 inUse=2"); + assertTrue(oliphaunts.contains(firstOliphaunt)); + assertTrue(oliphaunts.contains(secondOliphaunt)); + + } + + +} \ No newline at end of file diff --git a/object-pool/src/test/java/com/iluwatar/objectpool/AppTest.java b/object-pool/src/test/java/com/iluwatar/objectpool/AppTest.java deleted file mode 100644 index ba569cb96..000000000 --- a/object-pool/src/test/java/com/iluwatar/objectpool/AppTest.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.iluwatar.objectpool; - -import org.junit.Test; - -import com.iluwatar.objectpool.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} diff --git a/observer/README.md b/observer/README.md new file mode 100644 index 000000000..6fbe3cdab --- /dev/null +++ b/observer/README.md @@ -0,0 +1,42 @@ +--- +layout: pattern +title: Observer +folder: observer +permalink: /patterns/observer/ +categories: Behavioral +tags: + - Java + - Difficulty-Beginner + - Gang Of Four + - Reactive +--- + +## Also known as +Dependents, Publish-Subscribe + +## Intent +Define a one-to-many dependency between objects so that when one +object changes state, all its dependents are notified and updated +automatically. + +![alt text](./etc/observer_1.png "Observer") + +## Applicability +Use the Observer pattern in any of the following situations + +* when an abstraction has two aspects, one dependent on the other. Encapsulating these aspects in separate objects lets you vary and reuse them independently +* when a change to one object requires changing others, and you don't know how many objects need to be changed +* when an object should be able to notify other objects without making assumptions about who these objects are. In other words, you don't want these objects tightly coupled + +## Typical Use Case + +* changing in one object leads to a change in other objects + +## Real world examples + +* [java.util.Observer](http://docs.oracle.com/javase/8/docs/api/java/util/Observer.html) + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) +* [Java Generics and Collections](http://www.amazon.com/Java-Generics-Collections-Maurice-Naftalin/dp/0596527756/) diff --git a/observer/pom.xml b/observer/pom.xml index 77246fb13..847a7ea89 100644 --- a/observer/pom.xml +++ b/observer/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT observer @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/observer/src/main/java/com/iluwatar/observer/App.java b/observer/src/main/java/com/iluwatar/observer/App.java index 9d97084c8..dcced00d4 100644 --- a/observer/src/main/java/com/iluwatar/observer/App.java +++ b/observer/src/main/java/com/iluwatar/observer/App.java @@ -1,38 +1,71 @@ -package com.iluwatar.observer; - -import com.iluwatar.observer.generic.GHobbits; -import com.iluwatar.observer.generic.GOrcs; -import com.iluwatar.observer.generic.GWeather; - -/** - * - * Observer pattern defines one-to-many relationship between objects. The target - * object sends change notifications to its registered observers. - * - */ -public class App { - - public static void main(String[] args) { - - Weather weather = new Weather(); - weather.addObserver(new Orcs()); - weather.addObserver(new Hobbits()); - - weather.timePasses(); - weather.timePasses(); - weather.timePasses(); - weather.timePasses(); - - // Generic observer inspired by Java Generics and Collection by Naftalin & Wadler - System.out.println("\n--Running generic version--"); - GWeather gWeather = new GWeather(); - gWeather.addObserver(new GOrcs()); - gWeather.addObserver(new GHobbits()); - - gWeather.timePasses(); - gWeather.timePasses(); - gWeather.timePasses(); - gWeather.timePasses(); - - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.observer; + +import com.iluwatar.observer.generic.GHobbits; +import com.iluwatar.observer.generic.GOrcs; +import com.iluwatar.observer.generic.GWeather; + +/** + * + * The Observer pattern is a software design pattern in which an object, called the subject, + * maintains a list of its dependents, called observers, and notifies them automatically of any + * state changes, usually by calling one of their methods. It is mainly used to implement + * distributed event handling systems. The Observer pattern is also a key part in the familiar + * model–view–controller (MVC) architectural pattern. The Observer pattern is implemented in + * numerous programming libraries and systems, including almost all GUI toolkits. + *

+ * In this example {@link Weather} has a state that can be observed. The {@link Orcs} and + * {@link Hobbits} register as observers and receive notifications when the {@link Weather} changes. + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + + Weather weather = new Weather(); + weather.addObserver(new Orcs()); + weather.addObserver(new Hobbits()); + + weather.timePasses(); + weather.timePasses(); + weather.timePasses(); + weather.timePasses(); + + // Generic observer inspired by Java Generics and Collection by Naftalin & Wadler + System.out.println("\n--Running generic version--"); + GWeather gWeather = new GWeather(); + gWeather.addObserver(new GOrcs()); + gWeather.addObserver(new GHobbits()); + + gWeather.timePasses(); + gWeather.timePasses(); + gWeather.timePasses(); + gWeather.timePasses(); + } +} diff --git a/observer/src/main/java/com/iluwatar/observer/Hobbits.java b/observer/src/main/java/com/iluwatar/observer/Hobbits.java index b8a4f5b4f..ed9636bd6 100644 --- a/observer/src/main/java/com/iluwatar/observer/Hobbits.java +++ b/observer/src/main/java/com/iluwatar/observer/Hobbits.java @@ -1,25 +1,51 @@ -package com.iluwatar.observer; - -public class Hobbits implements WeatherObserver { - - @Override - public void update(WeatherType currentWeather) { - switch (currentWeather) { - case COLD: - System.out.println("The hobbits are shivering in the cold weather."); - break; - case RAINY: - System.out.println("The hobbits look for cover from the rain."); - break; - case SUNNY: - System.out.println("The happy hobbits bade in the warm sun."); - break; - case WINDY: - System.out.println("The hobbits hold their hats tightly in the windy weather."); - break; - default: - break; - } - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.observer; + +/** + * + * Hobbits + * + */ +public class Hobbits implements WeatherObserver { + + @Override + public void update(WeatherType currentWeather) { + switch (currentWeather) { + case COLD: + System.out.println("The hobbits are shivering in the cold weather."); + break; + case RAINY: + System.out.println("The hobbits look for cover from the rain."); + break; + case SUNNY: + System.out.println("The happy hobbits bade in the warm sun."); + break; + case WINDY: + System.out.println("The hobbits hold their hats tightly in the windy weather."); + break; + default: + break; + } + } +} diff --git a/observer/src/main/java/com/iluwatar/observer/Orcs.java b/observer/src/main/java/com/iluwatar/observer/Orcs.java index 0e51ab450..ce9c09944 100644 --- a/observer/src/main/java/com/iluwatar/observer/Orcs.java +++ b/observer/src/main/java/com/iluwatar/observer/Orcs.java @@ -1,25 +1,51 @@ -package com.iluwatar.observer; - -public class Orcs implements WeatherObserver { - - @Override - public void update(WeatherType currentWeather) { - switch (currentWeather) { - case COLD: - System.out.println("The orcs are freezing cold."); - break; - case RAINY: - System.out.println("The orcs are dripping wet."); - break; - case SUNNY: - System.out.println("The sun hurts the orcs' eyes."); - break; - case WINDY: - System.out.println("The orc smell almost vanishes in the wind."); - break; - default: - break; - } - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.observer; + +/** + * + * Orcs + * + */ +public class Orcs implements WeatherObserver { + + @Override + public void update(WeatherType currentWeather) { + switch (currentWeather) { + case COLD: + System.out.println("The orcs are freezing cold."); + break; + case RAINY: + System.out.println("The orcs are dripping wet."); + break; + case SUNNY: + System.out.println("The sun hurts the orcs' eyes."); + break; + case WINDY: + System.out.println("The orc smell almost vanishes in the wind."); + break; + default: + break; + } + } +} diff --git a/observer/src/main/java/com/iluwatar/observer/Weather.java b/observer/src/main/java/com/iluwatar/observer/Weather.java index 32ff13d03..f6aad3881 100644 --- a/observer/src/main/java/com/iluwatar/observer/Weather.java +++ b/observer/src/main/java/com/iluwatar/observer/Weather.java @@ -1,42 +1,67 @@ -package com.iluwatar.observer; - -import java.util.ArrayList; -import java.util.List; - -/** - * - * Weather can be observed by implementing WeatherObserver interface and - * registering as listener. - * - */ -public class Weather { - - private WeatherType currentWeather; - private List observers; - - public Weather() { - observers = new ArrayList<>(); - currentWeather = WeatherType.SUNNY; - } - - public void addObserver(WeatherObserver obs) { - observers.add(obs); - } - - public void removeObserver(WeatherObserver obs) { - observers.remove(obs); - } - - public void timePasses() { - WeatherType[] enumValues = WeatherType.values(); - currentWeather = enumValues[(currentWeather.ordinal() + 1) % enumValues.length]; - System.out.println("The weather changed to " + currentWeather + "."); - notifyObservers(); - } - - private void notifyObservers() { - for (WeatherObserver obs : observers) { - obs.update(currentWeather); - } - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.observer; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * Weather can be observed by implementing {@link WeatherObserver} interface and registering as + * listener. + * + */ +public class Weather { + + private WeatherType currentWeather; + private List observers; + + public Weather() { + observers = new ArrayList<>(); + currentWeather = WeatherType.SUNNY; + } + + public void addObserver(WeatherObserver obs) { + observers.add(obs); + } + + public void removeObserver(WeatherObserver obs) { + observers.remove(obs); + } + + /** + * Makes time pass for weather + */ + public void timePasses() { + WeatherType[] enumValues = WeatherType.values(); + currentWeather = enumValues[(currentWeather.ordinal() + 1) % enumValues.length]; + System.out.println("The weather changed to " + currentWeather + "."); + notifyObservers(); + } + + private void notifyObservers() { + for (WeatherObserver obs : observers) { + obs.update(currentWeather); + } + } +} diff --git a/observer/src/main/java/com/iluwatar/observer/WeatherObserver.java b/observer/src/main/java/com/iluwatar/observer/WeatherObserver.java index 5491da75e..afbf2a303 100644 --- a/observer/src/main/java/com/iluwatar/observer/WeatherObserver.java +++ b/observer/src/main/java/com/iluwatar/observer/WeatherObserver.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.observer; /** @@ -7,6 +29,6 @@ package com.iluwatar.observer; */ public interface WeatherObserver { - void update(WeatherType currentWeather); + void update(WeatherType currentWeather); } diff --git a/observer/src/main/java/com/iluwatar/observer/WeatherType.java b/observer/src/main/java/com/iluwatar/observer/WeatherType.java index b16c3cc04..b88452160 100644 --- a/observer/src/main/java/com/iluwatar/observer/WeatherType.java +++ b/observer/src/main/java/com/iluwatar/observer/WeatherType.java @@ -1,12 +1,38 @@ -package com.iluwatar.observer; - -public enum WeatherType { - - SUNNY, RAINY, WINDY, COLD; - - @Override - public String toString() { - return this.name().toLowerCase(); - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.observer; + +/** + * + * WeatherType enumeration + * + */ +public enum WeatherType { + + SUNNY, RAINY, WINDY, COLD; + + @Override + public String toString() { + return this.name().toLowerCase(); + } +} diff --git a/observer/src/main/java/com/iluwatar/observer/generic/GHobbits.java b/observer/src/main/java/com/iluwatar/observer/generic/GHobbits.java index 78caefbcf..da84b9aab 100644 --- a/observer/src/main/java/com/iluwatar/observer/generic/GHobbits.java +++ b/observer/src/main/java/com/iluwatar/observer/generic/GHobbits.java @@ -1,25 +1,52 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.observer.generic; import com.iluwatar.observer.WeatherType; +/** + * + * GHobbits + * + */ public class GHobbits implements Race { - @Override - public void update(GWeather weather, WeatherType weatherType) { - switch (weatherType) { - case COLD: - System.out.println("The hobbits are shivering in the cold weather."); - break; - case RAINY: - System.out.println("The hobbits look for cover from the rain."); - break; - case SUNNY: - System.out.println("The happy hobbits bade in the warm sun."); - break; - case WINDY: - System.out.println("The hobbits hold their hats tightly in the windy weather."); - break; - default: - break; - } + @Override + public void update(GWeather weather, WeatherType weatherType) { + switch (weatherType) { + case COLD: + System.out.println("The hobbits are shivering in the cold weather."); + break; + case RAINY: + System.out.println("The hobbits look for cover from the rain."); + break; + case SUNNY: + System.out.println("The happy hobbits bade in the warm sun."); + break; + case WINDY: + System.out.println("The hobbits hold their hats tightly in the windy weather."); + break; + default: + break; } + } } diff --git a/observer/src/main/java/com/iluwatar/observer/generic/GOrcs.java b/observer/src/main/java/com/iluwatar/observer/generic/GOrcs.java index 97518ee10..9f41aa6cc 100644 --- a/observer/src/main/java/com/iluwatar/observer/generic/GOrcs.java +++ b/observer/src/main/java/com/iluwatar/observer/generic/GOrcs.java @@ -1,25 +1,53 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.observer.generic; import com.iluwatar.observer.WeatherType; +/** + * + * GOrcs + * + */ public class GOrcs implements Race { - @Override - public void update(GWeather weather, WeatherType weatherType) { - switch (weatherType) { - case COLD: - System.out.println("The orcs are freezing cold."); - break; - case RAINY: - System.out.println("The orcs are dripping wet."); - break; - case SUNNY: - System.out.println("The sun hurts the orcs' eyes."); - break; - case WINDY: - System.out.println("The orc smell almost vanishes in the wind."); - break; - default: - break; - } + + @Override + public void update(GWeather weather, WeatherType weatherType) { + switch (weatherType) { + case COLD: + System.out.println("The orcs are freezing cold."); + break; + case RAINY: + System.out.println("The orcs are dripping wet."); + break; + case SUNNY: + System.out.println("The sun hurts the orcs' eyes."); + break; + case WINDY: + System.out.println("The orc smell almost vanishes in the wind."); + break; + default: + break; } + } } diff --git a/observer/src/main/java/com/iluwatar/observer/generic/GWeather.java b/observer/src/main/java/com/iluwatar/observer/generic/GWeather.java index c4702542c..137000760 100644 --- a/observer/src/main/java/com/iluwatar/observer/generic/GWeather.java +++ b/observer/src/main/java/com/iluwatar/observer/generic/GWeather.java @@ -1,19 +1,49 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.observer.generic; import com.iluwatar.observer.WeatherType; +/** + * + * GWeather + * + */ public class GWeather extends Observable { - private WeatherType currentWeather; + private WeatherType currentWeather; - public GWeather() { - currentWeather = WeatherType.SUNNY; - } + public GWeather() { + currentWeather = WeatherType.SUNNY; + } - public void timePasses() { - WeatherType[] enumValues = WeatherType.values(); - currentWeather = enumValues[(currentWeather.ordinal() + 1) % enumValues.length]; - System.out.println("The weather changed to " + currentWeather + "."); - notifyObservers(currentWeather); - } + /** + * Makes time pass for weather + */ + public void timePasses() { + WeatherType[] enumValues = WeatherType.values(); + currentWeather = enumValues[(currentWeather.ordinal() + 1) % enumValues.length]; + System.out.println("The weather changed to " + currentWeather + "."); + notifyObservers(currentWeather); + } } diff --git a/observer/src/main/java/com/iluwatar/observer/generic/Observable.java b/observer/src/main/java/com/iluwatar/observer/generic/Observable.java index eaedc7b6e..a9a6bf80d 100644 --- a/observer/src/main/java/com/iluwatar/observer/generic/Observable.java +++ b/observer/src/main/java/com/iluwatar/observer/generic/Observable.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.observer.generic; import java.util.List; @@ -12,20 +34,27 @@ import java.util.concurrent.CopyOnWriteArrayList; */ public abstract class Observable, O extends Observer, A> { - protected List observers; + protected List observers; - public Observable() { - this.observers = new CopyOnWriteArrayList<>(); - } + public Observable() { + this.observers = new CopyOnWriteArrayList<>(); + } - public void addObserver(O observer) { - this.observers.add(observer); - } + public void addObserver(O observer) { + this.observers.add(observer); + } - @SuppressWarnings("unchecked") - public void notifyObservers(A argument) { - for (O observer : observers) { - observer.update((S) this, argument); - } + public void removeObserver(O observer) { + this.observers.remove(observer); + } + + /** + * Notify observers + */ + @SuppressWarnings("unchecked") + public void notifyObservers(A argument) { + for (O observer : observers) { + observer.update((S) this, argument); } + } } diff --git a/observer/src/main/java/com/iluwatar/observer/generic/Observer.java b/observer/src/main/java/com/iluwatar/observer/generic/Observer.java index 901930de8..170f14ce8 100644 --- a/observer/src/main/java/com/iluwatar/observer/generic/Observer.java +++ b/observer/src/main/java/com/iluwatar/observer/generic/Observer.java @@ -1,6 +1,32 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.observer.generic; +/** + * + * Observer + */ public interface Observer, O extends Observer, A> { - void update(S subject, A argument); + void update(S subject, A argument); } diff --git a/observer/src/main/java/com/iluwatar/observer/generic/Race.java b/observer/src/main/java/com/iluwatar/observer/generic/Race.java index 358b27758..08ec22945 100644 --- a/observer/src/main/java/com/iluwatar/observer/generic/Race.java +++ b/observer/src/main/java/com/iluwatar/observer/generic/Race.java @@ -1,6 +1,33 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.observer.generic; import com.iluwatar.observer.WeatherType; +/** + * + * Race + * + */ public interface Race extends Observer { } diff --git a/observer/src/test/java/com/iluwatar/observer/AppTest.java b/observer/src/test/java/com/iluwatar/observer/AppTest.java index 9e208ce66..237726463 100644 --- a/observer/src/test/java/com/iluwatar/observer/AppTest.java +++ b/observer/src/test/java/com/iluwatar/observer/AppTest.java @@ -1,14 +1,39 @@ -package com.iluwatar.observer; - -import org.junit.Test; - -import com.iluwatar.observer.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.observer; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/observer/src/test/java/com/iluwatar/observer/HobbitsTest.java b/observer/src/test/java/com/iluwatar/observer/HobbitsTest.java new file mode 100644 index 000000000..8b670c56b --- /dev/null +++ b/observer/src/test/java/com/iluwatar/observer/HobbitsTest.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.observer; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Date: 12/27/15 - 12:07 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class HobbitsTest extends WeatherObserverTest { + + @Parameterized.Parameters + public static Collection data() { + final List testData = new ArrayList<>(); + testData.add(new Object[]{WeatherType.SUNNY, "The happy hobbits bade in the warm sun."}); + testData.add(new Object[]{WeatherType.RAINY, "The hobbits look for cover from the rain."}); + testData.add(new Object[]{WeatherType.WINDY, "The hobbits hold their hats tightly in the windy weather."}); + testData.add(new Object[]{WeatherType.COLD, "The hobbits are shivering in the cold weather."}); + return testData; + } + + /** + * Create a new test with the given weather and expected response + * + * @param weather The weather that should be unleashed on the observer + * @param response The expected response from the observer + */ + public HobbitsTest(final WeatherType weather, final String response) { + super(weather, response, Hobbits::new); + } + +} diff --git a/observer/src/test/java/com/iluwatar/observer/OrcsTest.java b/observer/src/test/java/com/iluwatar/observer/OrcsTest.java new file mode 100644 index 000000000..a7997eaa7 --- /dev/null +++ b/observer/src/test/java/com/iluwatar/observer/OrcsTest.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.observer; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Date: 12/27/15 - 12:07 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class OrcsTest extends WeatherObserverTest { + + @Parameterized.Parameters + public static Collection data() { + final List testData = new ArrayList<>(); + testData.add(new Object[]{WeatherType.SUNNY, "The sun hurts the orcs' eyes."}); + testData.add(new Object[]{WeatherType.RAINY, "The orcs are dripping wet."}); + testData.add(new Object[]{WeatherType.WINDY, "The orc smell almost vanishes in the wind."}); + testData.add(new Object[]{WeatherType.COLD, "The orcs are freezing cold."}); + return testData; + } + + /** + * Create a new test with the given weather and expected response + * + * @param weather The weather that should be unleashed on the observer + * @param response The expected response from the observer + */ + public OrcsTest(final WeatherType weather, final String response) { + super(weather, response, Orcs::new); + } + +} diff --git a/observer/src/test/java/com/iluwatar/observer/StdOutTest.java b/observer/src/test/java/com/iluwatar/observer/StdOutTest.java new file mode 100644 index 000000000..afd870ae4 --- /dev/null +++ b/observer/src/test/java/com/iluwatar/observer/StdOutTest.java @@ -0,0 +1,76 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.observer; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/27/15 - 12:16 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since changes in the weather doesn't has + * any influence on any other accessible objects, except for writing to std-out using {@link + * System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + protected final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/observer/src/test/java/com/iluwatar/observer/WeatherObserverTest.java b/observer/src/test/java/com/iluwatar/observer/WeatherObserverTest.java new file mode 100644 index 000000000..a06c19952 --- /dev/null +++ b/observer/src/test/java/com/iluwatar/observer/WeatherObserverTest.java @@ -0,0 +1,81 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.observer; + +import org.junit.Test; + +import java.util.function.Supplier; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/27/15 - 11:44 AM + * + * @author Jeroen Meulemeester + */ +public abstract class WeatherObserverTest extends StdOutTest { + + /** + * The observer instance factory + */ + private final Supplier factory; + + /** + * The weather type currently tested + */ + private final WeatherType weather; + + /** + * The expected response from the observer + */ + private final String response; + + /** + * Create a new test instance using the given parameters + * + * @param weather The weather currently being tested + * @param response The expected response from the observer + * @param factory The factory, used to create an instance of the tested observer + */ + WeatherObserverTest(final WeatherType weather, final String response, final Supplier factory) { + this.weather = weather; + this.response = response; + this.factory = factory; + } + + /** + * Verify if the weather has the expected influence on the observer + */ + @Test + public void testObserver() { + final O observer = this.factory.get(); + verifyZeroInteractions(getStdOutMock()); + + observer.update(this.weather); + verify(getStdOutMock()).println(this.response); + verifyNoMoreInteractions(getStdOutMock()); + } + +} \ No newline at end of file diff --git a/observer/src/test/java/com/iluwatar/observer/WeatherTest.java b/observer/src/test/java/com/iluwatar/observer/WeatherTest.java new file mode 100644 index 000000000..7a38e7137 --- /dev/null +++ b/observer/src/test/java/com/iluwatar/observer/WeatherTest.java @@ -0,0 +1,83 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.observer; + +import org.junit.Test; +import org.mockito.InOrder; + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/27/15 - 11:08 AM + * + * @author Jeroen Meulemeester + */ +public class WeatherTest extends StdOutTest { + + /** + * Add a {@link WeatherObserver}, verify if it gets notified of a weather change, remove the + * observer again and verify that there are no more notifications. + */ + @Test + public void testAddRemoveObserver() { + final WeatherObserver observer = mock(WeatherObserver.class); + + final Weather weather = new Weather(); + weather.addObserver(observer); + verifyZeroInteractions(observer); + + weather.timePasses(); + verify(getStdOutMock()).println("The weather changed to rainy."); + verify(observer).update(WeatherType.RAINY); + + weather.removeObserver(observer); + weather.timePasses(); + verify(getStdOutMock()).println("The weather changed to windy."); + + verifyNoMoreInteractions(observer, getStdOutMock()); + } + + /** + * Verify if the weather passes in the order of the {@link WeatherType}s + */ + @Test + public void testTimePasses() { + final WeatherObserver observer = mock(WeatherObserver.class); + final Weather weather = new Weather(); + weather.addObserver(observer); + + final InOrder inOrder = inOrder(observer, getStdOutMock()); + final WeatherType[] weatherTypes = WeatherType.values(); + for (int i = 1; i < 20; i++) { + weather.timePasses(); + inOrder.verify(observer).update(weatherTypes[i % weatherTypes.length]); + } + + verifyNoMoreInteractions(observer); + } + +} \ No newline at end of file diff --git a/observer/src/test/java/com/iluwatar/observer/generic/GHobbitsTest.java b/observer/src/test/java/com/iluwatar/observer/generic/GHobbitsTest.java new file mode 100644 index 000000000..7668daee6 --- /dev/null +++ b/observer/src/test/java/com/iluwatar/observer/generic/GHobbitsTest.java @@ -0,0 +1,64 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.observer.generic; + +import com.iluwatar.observer.Hobbits; +import com.iluwatar.observer.WeatherObserverTest; +import com.iluwatar.observer.WeatherType; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Date: 12/27/15 - 12:07 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class GHobbitsTest extends ObserverTest { + + @Parameterized.Parameters + public static Collection data() { + final List testData = new ArrayList<>(); + testData.add(new Object[]{WeatherType.SUNNY, "The happy hobbits bade in the warm sun."}); + testData.add(new Object[]{WeatherType.RAINY, "The hobbits look for cover from the rain."}); + testData.add(new Object[]{WeatherType.WINDY, "The hobbits hold their hats tightly in the windy weather."}); + testData.add(new Object[]{WeatherType.COLD, "The hobbits are shivering in the cold weather."}); + return testData; + } + + /** + * Create a new test with the given weather and expected response + * + * @param weather The weather that should be unleashed on the observer + * @param response The expected response from the observer + */ + public GHobbitsTest(final WeatherType weather, final String response) { + super(weather, response, GHobbits::new); + } + +} diff --git a/observer/src/test/java/com/iluwatar/observer/generic/GWeatherTest.java b/observer/src/test/java/com/iluwatar/observer/generic/GWeatherTest.java new file mode 100644 index 000000000..758dbca8c --- /dev/null +++ b/observer/src/test/java/com/iluwatar/observer/generic/GWeatherTest.java @@ -0,0 +1,87 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.observer.generic; + +import com.iluwatar.observer.StdOutTest; +import com.iluwatar.observer.WeatherObserver; +import com.iluwatar.observer.WeatherType; + +import org.junit.Test; +import org.mockito.InOrder; + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/27/15 - 11:08 AM + * + * @author Jeroen Meulemeester + */ +public class GWeatherTest extends StdOutTest { + + /** + * Add a {@link WeatherObserver}, verify if it gets notified of a weather change, remove the + * observer again and verify that there are no more notifications. + */ + @Test + public void testAddRemoveObserver() { + final Race observer = mock(Race.class); + + final GWeather weather = new GWeather(); + weather.addObserver(observer); + verifyZeroInteractions(observer); + + weather.timePasses(); + verify(getStdOutMock()).println("The weather changed to rainy."); + verify(observer).update(weather, WeatherType.RAINY); + + weather.removeObserver(observer); + weather.timePasses(); + verify(getStdOutMock()).println("The weather changed to windy."); + + verifyNoMoreInteractions(observer, getStdOutMock()); + } + + /** + * Verify if the weather passes in the order of the {@link WeatherType}s + */ + @Test + public void testTimePasses() { + final Race observer = mock(Race.class); + final GWeather weather = new GWeather(); + weather.addObserver(observer); + + final InOrder inOrder = inOrder(observer, getStdOutMock()); + final WeatherType[] weatherTypes = WeatherType.values(); + for (int i = 1; i < 20; i++) { + weather.timePasses(); + inOrder.verify(observer).update(weather, weatherTypes[i % weatherTypes.length]); + } + + verifyNoMoreInteractions(observer); + } + +} \ No newline at end of file diff --git a/observer/src/test/java/com/iluwatar/observer/generic/ObserverTest.java b/observer/src/test/java/com/iluwatar/observer/generic/ObserverTest.java new file mode 100644 index 000000000..33d8daaf5 --- /dev/null +++ b/observer/src/test/java/com/iluwatar/observer/generic/ObserverTest.java @@ -0,0 +1,84 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.observer.generic; + +import com.iluwatar.observer.StdOutTest; +import com.iluwatar.observer.WeatherType; + +import org.junit.Test; + +import java.util.function.Supplier; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/27/15 - 11:44 AM + * + * @author Jeroen Meulemeester + */ +public abstract class ObserverTest extends StdOutTest { + + /** + * The observer instance factory + */ + private final Supplier factory; + + /** + * The weather type currently tested + */ + private final WeatherType weather; + + /** + * The expected response from the observer + */ + private final String response; + + /** + * Create a new test instance using the given parameters + * + * @param weather The weather currently being tested + * @param response The expected response from the observer + * @param factory The factory, used to create an instance of the tested observer + */ + ObserverTest(final WeatherType weather, final String response, final Supplier factory) { + this.weather = weather; + this.response = response; + this.factory = factory; + } + + /** + * Verify if the weather has the expected influence on the observer + */ + @Test + public void testObserver() { + final O observer = this.factory.get(); + verifyZeroInteractions(getStdOutMock()); + + observer.update(null, this.weather); + verify(getStdOutMock()).println(this.response); + verifyNoMoreInteractions(getStdOutMock()); + } + +} \ No newline at end of file diff --git a/observer/src/test/java/com/iluwatar/observer/generic/OrcsTest.java b/observer/src/test/java/com/iluwatar/observer/generic/OrcsTest.java new file mode 100644 index 000000000..9ac1bddea --- /dev/null +++ b/observer/src/test/java/com/iluwatar/observer/generic/OrcsTest.java @@ -0,0 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.observer.generic; + +import com.iluwatar.observer.WeatherType; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Date: 12/27/15 - 12:07 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class OrcsTest extends ObserverTest { + + @Parameterized.Parameters + public static Collection data() { + final List testData = new ArrayList<>(); + testData.add(new Object[]{WeatherType.SUNNY, "The sun hurts the orcs' eyes."}); + testData.add(new Object[]{WeatherType.RAINY, "The orcs are dripping wet."}); + testData.add(new Object[]{WeatherType.WINDY, "The orc smell almost vanishes in the wind."}); + testData.add(new Object[]{WeatherType.COLD, "The orcs are freezing cold."}); + return testData; + } + + /** + * Create a new test with the given weather and expected response + * + * @param weather The weather that should be unleashed on the observer + * @param response The expected response from the observer + */ + public OrcsTest(final WeatherType weather, final String response) { + super(weather, response, GOrcs::new); + } + +} diff --git a/page-object/README.md b/page-object/README.md new file mode 100644 index 000000000..b4f8246f1 --- /dev/null +++ b/page-object/README.md @@ -0,0 +1,31 @@ +--- +layout: pattern +title: Page Object +folder: page-object +permalink: /patterns/page-object/ +categories: Testing +tags: +- Java +- Difficulty-Intermediate +--- + +## Intent + +Page Object encapsulates the UI, hiding the underlying UI widgetry of an application (commonly a web application) and providing an application-specific API to allow the manipulation of UI components required for tests. In doing so, it allows the test class itself to focus on the test logic instead. + + +![alt text](./etc/page-object.png "Page Object") + + +## Applicability + +Use the Page Object pattern when + +* You are writing automated tests for your web application and you want to separate the UI manipulation required for the tests from the actual test logic. +* Make your tests less brittle, and more readable and robust + +## Credits + +* [Martin Fowler - PageObject](http://martinfowler.com/bliki/PageObject.html) +* [Selenium - Page Objects](https://github.com/SeleniumHQ/selenium/wiki/PageObjects) + diff --git a/page-object/etc/page-object.png b/page-object/etc/page-object.png new file mode 100644 index 000000000..4240b438e Binary files /dev/null and b/page-object/etc/page-object.png differ diff --git a/page-object/etc/page-object.ucls b/page-object/etc/page-object.ucls new file mode 100644 index 000000000..466c542fd --- /dev/null +++ b/page-object/etc/page-object.ucls @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/page-object/pom.xml b/page-object/pom.xml new file mode 100644 index 000000000..e6f888b05 --- /dev/null +++ b/page-object/pom.xml @@ -0,0 +1,46 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + page-object + + + junit + junit + test + + + net.sourceforge.htmlunit + htmlunit + + + diff --git a/page-object/src/main/java/com/iluwatar/pageobject/App.java b/page-object/src/main/java/com/iluwatar/pageobject/App.java new file mode 100644 index 000000000..235eedd25 --- /dev/null +++ b/page-object/src/main/java/com/iluwatar/pageobject/App.java @@ -0,0 +1,89 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.pageobject; + +import java.awt.Desktop; +import java.io.File; +import java.io.IOException; + +/** + * Page Object pattern wraps an UI component with an application specific API allowing you to + * manipulate the UI elements without having to dig around with the underlying UI technology used. This is + * especially useful for testing as it means your tests will be less brittle. Your tests can concentrate on + * the actual test cases where as the manipulation of the UI can be left to the internals of the page object + * itself. + * + *

+ * Due to this reason, it has become very popular within the test automation community. + * In particular, it is very common in that the page object is used to represent the html pages of a + * web application that is under test. This web application is referred to as AUT (Application Under Test). + * A web browser automation tool/framework like Selenium for instance, is then used to drive the automating + * of the browser navigation and user actions journeys through this web application. Your test class would + * therefore only be responsible for particular test cases and page object would be used by the test class + * for UI manipulation required for the tests. + * + *

+ * In this implementation rather than using Selenium, the HtmlUnit library is used as a replacement to + * represent the specific html elements and to drive the browser. The purpose of this example is just to + * provide a simple version that showcase the intentions of this pattern and how this pattern is used + * in order to understand it. + */ +public final class App { + + private App() { + } + + /** + * Application entry point + * + *

+ * The application under development is a web application. Normally you would probably have a + * backend that is probably implemented in an object-oriented language (e.g. Java) that serves + * the frontend which comprises of a series of HTML, CSS, JS etc... + * + *

+ * For illustrations purposes only, a very simple static html app is used here. This main method + * just fires up this simple web app in a default browser. + * + * @param args arguments + */ + public static void main(String[] args) { + + try { + File applicationFile = new File(App.class.getClassLoader().getResource("sample-ui/login.html").getPath()); + + // should work for unix like OS (mac, unix etc...) + if (Desktop.isDesktopSupported()) { + Desktop.getDesktop().open(applicationFile); + + } else { + // java Desktop not supported - above unlikely to work for Windows so try following instead... + Runtime.getRuntime().exec("cmd.exe start " + applicationFile); + } + + } catch (IOException ex) { + ex.printStackTrace(); + } + + } +} diff --git a/page-object/src/main/resources/sample-ui/album-list.html b/page-object/src/main/resources/sample-ui/album-list.html new file mode 100644 index 000000000..e02e65cf3 --- /dev/null +++ b/page-object/src/main/resources/sample-ui/album-list.html @@ -0,0 +1,60 @@ + + + + + + Album List + + + + +

+

My Album Viewer

+
+ +
+
+ + + + + + + + + + + + + + + +
Album TitleAlbum YearAlbum RatingNumber of SongsArtist
212011A11Adele
+
+
+ + + \ No newline at end of file diff --git a/page-object/src/main/resources/sample-ui/album-page.html b/page-object/src/main/resources/sample-ui/album-page.html new file mode 100644 index 000000000..5bf843be2 --- /dev/null +++ b/page-object/src/main/resources/sample-ui/album-page.html @@ -0,0 +1,74 @@ + + + + + + Album Page + + + +
+

21

+
+ +
+
+
+ + + + + + + + + + + + + + + + + + + +
Title:
Artist:
Year: + +
Rating:
Number of Songs:
+
+
+
+ + + \ No newline at end of file diff --git a/page-object/src/main/resources/sample-ui/css/album-list.css b/page-object/src/main/resources/sample-ui/css/album-list.css new file mode 100644 index 000000000..61fd0af81 --- /dev/null +++ b/page-object/src/main/resources/sample-ui/css/album-list.css @@ -0,0 +1,68 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +table { + font-size: 16px; + border-collapse: collapse; +} + +th { + background-color: #FFFFFF; + border: 1px solid black; + color: black; + width: 150px; + height: 20px; +} + +td { + border: 1px solid black; + background-color: white; +} + +th, td { + padding: 15px; + text-align: left; +} \ No newline at end of file diff --git a/page-object/src/main/resources/sample-ui/css/style.css b/page-object/src/main/resources/sample-ui/css/style.css new file mode 100644 index 000000000..691bba77f --- /dev/null +++ b/page-object/src/main/resources/sample-ui/css/style.css @@ -0,0 +1,49 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +body { + font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; +} \ No newline at end of file diff --git a/page-object/src/main/resources/sample-ui/login.html b/page-object/src/main/resources/sample-ui/login.html new file mode 100644 index 000000000..1d7786057 --- /dev/null +++ b/page-object/src/main/resources/sample-ui/login.html @@ -0,0 +1,48 @@ + + + + + + Login + + + +
+

Login

+
+ +
+
+ + + + +
Username:
Password:
+ +
+
+ + \ No newline at end of file diff --git a/page-object/src/test/java/com/iluwatar/pageobject/AlbumListPageTest.java b/page-object/src/test/java/com/iluwatar/pageobject/AlbumListPageTest.java new file mode 100644 index 000000000..b97410036 --- /dev/null +++ b/page-object/src/test/java/com/iluwatar/pageobject/AlbumListPageTest.java @@ -0,0 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.pageobject; + + +import com.gargoylesoftware.htmlunit.WebClient; +import com.iluwatar.pageobject.pages.AlbumListPage; +import com.iluwatar.pageobject.pages.AlbumPage; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + + +public class AlbumListPageTest { + + private AlbumListPage albumListPage = new AlbumListPage(new WebClient()); + + @Before + public void setUp() { + albumListPage.navigateToPage(); + } + + @Test + public void testSelectAlbum() { + AlbumPage albumPage = albumListPage.selectAlbum("21"); + albumPage.navigateToPage(); + assertTrue(albumPage.isAt()); + } + +} diff --git a/page-object/src/test/java/com/iluwatar/pageobject/AlbumPageTest.java b/page-object/src/test/java/com/iluwatar/pageobject/AlbumPageTest.java new file mode 100644 index 000000000..01461cc8d --- /dev/null +++ b/page-object/src/test/java/com/iluwatar/pageobject/AlbumPageTest.java @@ -0,0 +1,64 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.pageobject; + +import com.gargoylesoftware.htmlunit.WebClient; +import com.iluwatar.pageobject.pages.AlbumListPage; +import com.iluwatar.pageobject.pages.AlbumPage; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class AlbumPageTest { + + private AlbumPage albumPage = new AlbumPage(new WebClient()); + + @Before + public void setUp() { + albumPage.navigateToPage(); + } + + @Test + public void testSaveAlbum() { + + AlbumPage albumPageAfterChanges = albumPage + .changeAlbumTitle("25") + .changeArtist("Adele Laurie Blue Adkins") + .changeAlbumYear(2015) + .changeAlbumRating("B") + .changeNumberOfSongs(20) + .saveChanges(); + + assertTrue(albumPageAfterChanges.isAt()); + + } + + @Test + public void testCancelChanges() { + AlbumListPage albumListPage = albumPage.cancelChanges(); + albumListPage.navigateToPage(); + assertTrue(albumListPage.isAt()); + } + +} diff --git a/page-object/src/test/java/com/iluwatar/pageobject/LoginPageTest.java b/page-object/src/test/java/com/iluwatar/pageobject/LoginPageTest.java new file mode 100644 index 000000000..659df0629 --- /dev/null +++ b/page-object/src/test/java/com/iluwatar/pageobject/LoginPageTest.java @@ -0,0 +1,52 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.pageobject; + +import com.gargoylesoftware.htmlunit.WebClient; +import com.iluwatar.pageobject.pages.AlbumListPage; +import com.iluwatar.pageobject.pages.LoginPage; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class LoginPageTest { + + private LoginPage loginPage = new LoginPage(new WebClient()); + + @Before + public void setUp() { + loginPage.navigateToPage(); + } + + @Test + public void testLogin() { + AlbumListPage albumListPage = loginPage + .enterUsername("admin") + .enterPassword("password") + .login(); + albumListPage.navigateToPage(); + assertTrue(albumListPage.isAt()); + } + +} diff --git a/page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumListPage.java b/page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumListPage.java new file mode 100644 index 000000000..fb322ff53 --- /dev/null +++ b/page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumListPage.java @@ -0,0 +1,96 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.pageobject.pages; + +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.HtmlAnchor; +import com.gargoylesoftware.htmlunit.html.HtmlPage; + +import java.io.IOException; +import java.util.List; + +/** + * Page Object encapsulating the Album List page (album-list.html) + */ +public class AlbumListPage extends Page { + + private static final String ALBUM_LIST_HTML_FILE = "album-list.html"; + private static final String PAGE_URL = "file:" + AUT_PATH + ALBUM_LIST_HTML_FILE; + + private HtmlPage page; + + + /** + * Constructor + */ + public AlbumListPage(WebClient webClient) { + super(webClient); + } + + + /** + * Navigates to the Album List Page + * + * @return {@link AlbumListPage} + */ + public AlbumListPage navigateToPage() { + try { + page = this.webClient.getPage(PAGE_URL); + } catch (IOException e) { + e.printStackTrace(); + } + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isAt() { + return "Album List".equals(page.getTitleText()); + } + + /** + * Selects an album by the given album title + * + * @param albumTitle the title of the album to click + * @return the album page + */ + public AlbumPage selectAlbum(String albumTitle) { + // uses XPath to find list of html anchor tags with the class album in it + List albumLinks = (List) page.getByXPath("//tr[@class='album']//a"); + for (HtmlAnchor anchor : albumLinks) { + if (anchor.getTextContent().equals(albumTitle)) { + try { + anchor.click(); + return new AlbumPage(webClient); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + throw new IllegalArgumentException("No links with the album title: " + albumTitle); + } + + +} diff --git a/page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumPage.java b/page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumPage.java new file mode 100644 index 000000000..4dffb93dd --- /dev/null +++ b/page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumPage.java @@ -0,0 +1,174 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.pageobject.pages; + +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.HtmlNumberInput; +import com.gargoylesoftware.htmlunit.html.HtmlOption; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlSelect; +import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; +import com.gargoylesoftware.htmlunit.html.HtmlTextInput; + +import java.io.IOException; + +/** + * Page Object encapsulating the Album Page (album-page.html) + */ +public class AlbumPage extends Page { + + private static final String ALBUM_PAGE_HTML_FILE = "album-page.html"; + private static final String PAGE_URL = "file:" + AUT_PATH + ALBUM_PAGE_HTML_FILE; + + private HtmlPage page; + + + /** + * Constructor + */ + public AlbumPage(WebClient webClient) { + super(webClient); + } + + + /** + * Navigates to the album page + * + * @return {@link AlbumPage} + */ + public AlbumPage navigateToPage() { + try { + page = this.webClient.getPage(PAGE_URL); + } catch (IOException e) { + e.printStackTrace(); + } + return this; + } + + + /** + * {@inheritDoc} + */ + @Override + public boolean isAt() { + return "Album Page".equals(page.getTitleText()); + } + + + /** + * Sets the album title input text field + * + * @param albumTitle the new album title value to set + * @return {@link AlbumPage} + */ + public AlbumPage changeAlbumTitle(String albumTitle) { + HtmlTextInput albumTitleInputTextField = (HtmlTextInput) page.getElementById("albumTitle"); + albumTitleInputTextField.setText(albumTitle); + return this; + } + + + /** + * Sets the artist input text field + * + * @param artist the new artist value to set + * @return {@link AlbumPage} + */ + public AlbumPage changeArtist(String artist) { + HtmlTextInput artistInputTextField = (HtmlTextInput) page.getElementById("albumArtist"); + artistInputTextField.setText(artist); + return this; + } + + + /** + * Selects the select's option value based on the year value given + * + * @param year the new year value to set + * @return {@link AlbumPage} + */ + public AlbumPage changeAlbumYear(int year) { + HtmlSelect albumYearSelectOption = (HtmlSelect) page.getElementById("albumYear"); + HtmlOption yearOption = albumYearSelectOption.getOptionByValue(Integer.toString(year)); + albumYearSelectOption.setSelectedAttribute(yearOption, true); + return this; + } + + + /** + * Sets the album rating input text field + * + * @param albumRating the new album rating value to set + * @return {@link AlbumPage} + */ + public AlbumPage changeAlbumRating(String albumRating) { + HtmlTextInput albumRatingInputTextField = (HtmlTextInput) page.getElementById("albumRating"); + albumRatingInputTextField.setText(albumRating); + return this; + } + + /** + * Sets the number of songs number input field + * + * @param numberOfSongs the new number of songs value to be set + * @return {@link AlbumPage} + */ + public AlbumPage changeNumberOfSongs(int numberOfSongs) { + HtmlNumberInput numberOfSongsNumberField = (HtmlNumberInput) page.getElementById("numberOfSongs"); + numberOfSongsNumberField.setText(Integer.toString(numberOfSongs)); + return this; + } + + + /** + * Cancel changes made by clicking the cancel button + * + * @return {@link AlbumListPage} + */ + public AlbumListPage cancelChanges() { + HtmlSubmitInput cancelButton = (HtmlSubmitInput) page.getElementById("cancelButton"); + try { + cancelButton.click(); + } catch (IOException e) { + e.printStackTrace(); + } + return new AlbumListPage(webClient); + } + + + /** + * Saves changes made by clicking the save button + * + * @return {@link AlbumPage} + */ + public AlbumPage saveChanges() { + HtmlSubmitInput saveButton = (HtmlSubmitInput) page.getElementById("saveButton"); + try { + saveButton.click(); + } catch (IOException e) { + e.printStackTrace(); + } + return this; + } + +} diff --git a/page-object/src/test/java/com/iluwatar/pageobject/pages/LoginPage.java b/page-object/src/test/java/com/iluwatar/pageobject/pages/LoginPage.java new file mode 100644 index 000000000..d4bd0850c --- /dev/null +++ b/page-object/src/test/java/com/iluwatar/pageobject/pages/LoginPage.java @@ -0,0 +1,116 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.pageobject.pages; + +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput; +import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; +import com.gargoylesoftware.htmlunit.html.HtmlTextInput; +import java.io.IOException; + +/** + * Page Object encapsulating the Login Page (login.html) + */ +public class LoginPage extends Page { + + private static final String LOGIN_PAGE_HTML_FILE = "login.html"; + private static final String PAGE_URL = "file:" + AUT_PATH + LOGIN_PAGE_HTML_FILE; + + private HtmlPage page; + + /** + * Constructor + * + * @param webClient {@link WebClient} + */ + public LoginPage(WebClient webClient) { + super(webClient); + } + + /** + * Navigates to the Login page + * + * @return {@link LoginPage} + */ + public LoginPage navigateToPage() { + try { + page = this.webClient.getPage(PAGE_URL); + } catch (IOException e) { + e.printStackTrace(); + } + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isAt() { + return "Login".equals(page.getTitleText()); + } + + + /** + * Enters the username into the username input text field + * + * @param username the username to enter + * @return {@link LoginPage} + */ + public LoginPage enterUsername(String username) { + HtmlTextInput usernameInputTextField = (HtmlTextInput) page.getElementById("username"); + usernameInputTextField.setText(username); + return this; + } + + + /** + * Enters the password into the password input password field + * + * @param password the password to enter + * @return {@link LoginPage} + */ + public LoginPage enterPassword(String password) { + HtmlPasswordInput passwordInputPasswordField = (HtmlPasswordInput) page.getElementById("password"); + passwordInputPasswordField.setText(password); + return this; + } + + + /** + * Clicking on the login button to 'login' + * + * @return {@link AlbumListPage} + * - this is the page that user gets navigated to once successfully logged in + */ + public AlbumListPage login() { + HtmlSubmitInput loginButton = (HtmlSubmitInput) page.getElementById("loginButton"); + try { + loginButton.click(); + } catch (IOException e) { + e.printStackTrace(); + } + return new AlbumListPage(webClient); + } + +} diff --git a/page-object/src/test/java/com/iluwatar/pageobject/pages/Page.java b/page-object/src/test/java/com/iluwatar/pageobject/pages/Page.java new file mode 100644 index 000000000..b0b328e7c --- /dev/null +++ b/page-object/src/test/java/com/iluwatar/pageobject/pages/Page.java @@ -0,0 +1,57 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.pageobject.pages; + +import com.gargoylesoftware.htmlunit.WebClient; + +/** + * Encapsulation for a generic 'Page' + */ +public abstract class Page { + + /** + * Application Under Test path + * This directory location is where html web pages are located + */ + public static final String AUT_PATH = "src/main/resources/sample-ui/"; + + protected final WebClient webClient; + + /** + * Constructor + * + * @param webClient {@link WebClient} + */ + public Page(WebClient webClient) { + this.webClient = webClient; + } + + /** + * Checks that the current page is actually the page this page object represents + * + * @return true if so, otherwise false + */ + public abstract boolean isAt(); + + +} diff --git a/poison-pill/README.md b/poison-pill/README.md new file mode 100644 index 000000000..0815b376e --- /dev/null +++ b/poison-pill/README.md @@ -0,0 +1,26 @@ +--- +layout: pattern +title: Poison Pill +folder: poison-pill +permalink: /patterns/poison-pill/ +categories: Other +tags: + - Java + - Difficulty-Intermediate + - Reactive +--- + +## Intent +Poison Pill is known predefined data item that allows to provide +graceful shutdown for separate distributed consumption process. + +![alt text](./etc/poison-pill.png "Poison Pill") + +## Applicability +Use the Poison Pill idiom when + +* need to send signal from one thread/process to another to terminate + +## Real world examples + +* [akka.actor.PoisonPill](http://doc.akka.io/docs/akka/2.1.4/java/untyped-actors.html) diff --git a/poison-pill/pom.xml b/poison-pill/pom.xml index 10887dac5..9717f5e13 100644 --- a/poison-pill/pom.xml +++ b/poison-pill/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT poison-pill @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/poison-pill/src/main/java/com/iluwatar/poison/pill/App.java b/poison-pill/src/main/java/com/iluwatar/poison/pill/App.java new file mode 100644 index 000000000..88be9b05f --- /dev/null +++ b/poison-pill/src/main/java/com/iluwatar/poison/pill/App.java @@ -0,0 +1,67 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.poison.pill; + +/** + * One of the possible approaches to terminate Producer-Consumer pattern is using the Poison Pill + * idiom. If you use Poison Pill as the termination signal then Producer is responsible to notify + * Consumer that the exchange is over and reject any further messages. The Consumer receiving Poison + * Pill will stop reading messages from the queue. You must also ensure that the Poison Pill will be + * the last message that will be read from the queue (if you have prioritized queue then this can be + * tricky). + *

+ * In simple cases the Poison Pill can be just a null-reference, but holding a unique separate + * shared object-marker (with name "Poison" or "Poison Pill") is more clear and self describing. + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + MessageQueue queue = new SimpleMessageQueue(10000); + + final Producer producer = new Producer("PRODUCER_1", queue); + final Consumer consumer = new Consumer("CONSUMER_1", queue); + + new Thread() { + @Override + public void run() { + consumer.consume(); + } + }.start(); + + new Thread() { + @Override + public void run() { + producer.send("hand shake"); + producer.send("some very important information"); + producer.send("bye!"); + producer.stop(); + } + }.start(); + } +} diff --git a/poison-pill/src/main/java/com/iluwatar/poison/pill/Consumer.java b/poison-pill/src/main/java/com/iluwatar/poison/pill/Consumer.java new file mode 100644 index 000000000..c811225e1 --- /dev/null +++ b/poison-pill/src/main/java/com/iluwatar/poison/pill/Consumer.java @@ -0,0 +1,64 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.poison.pill; + +import com.iluwatar.poison.pill.Message.Headers; + +/** + * Class responsible for receiving and handling submitted to the queue messages + */ +public class Consumer { + + private final MqSubscribePoint queue; + private final String name; + + public Consumer(String name, MqSubscribePoint queue) { + this.name = name; + this.queue = queue; + } + + /** + * Consume message + */ + public void consume() { + while (true) { + Message msg; + try { + msg = queue.take(); + if (Message.POISON_PILL.equals(msg)) { + System.out.println(String.format("Consumer %s receive request to terminate.", name)); + break; + } + } catch (InterruptedException e) { + // allow thread to exit + System.err.println(e); + return; + } + + String sender = msg.getHeader(Headers.SENDER); + String body = msg.getBody(); + System.out.println(String.format("Message [%s] from [%s] received by [%s]", body, sender, + name)); + } + } +} diff --git a/poison-pill/src/main/java/com/iluwatar/poison/pill/Message.java b/poison-pill/src/main/java/com/iluwatar/poison/pill/Message.java new file mode 100644 index 000000000..c2f6b57bd --- /dev/null +++ b/poison-pill/src/main/java/com/iluwatar/poison/pill/Message.java @@ -0,0 +1,79 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.poison.pill; + +import java.util.Map; + +/** + * Interface that implements the Message pattern and represents an inbound or outbound message as + * part of an {@link Producer}-{@link Consumer} exchange. + */ +public interface Message { + + Message POISON_PILL = new Message() { + + @Override + public void addHeader(Headers header, String value) { + throw poison(); + } + + @Override + public String getHeader(Headers header) { + throw poison(); + } + + @Override + public Map getHeaders() { + throw poison(); + } + + @Override + public void setBody(String body) { + throw poison(); + } + + @Override + public String getBody() { + throw poison(); + } + + private RuntimeException poison() { + return new UnsupportedOperationException("Poison"); + } + + }; + + public enum Headers { + DATE, SENDER + } + + void addHeader(Headers header, String value); + + String getHeader(Headers header); + + Map getHeaders(); + + void setBody(String body); + + String getBody(); +} diff --git a/poison-pill/src/main/java/com/iluwatar/poison/pill/MessageQueue.java b/poison-pill/src/main/java/com/iluwatar/poison/pill/MessageQueue.java new file mode 100644 index 000000000..1f774aeff --- /dev/null +++ b/poison-pill/src/main/java/com/iluwatar/poison/pill/MessageQueue.java @@ -0,0 +1,30 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.poison.pill; + +/** + * Represents abstraction of channel (or pipe) that bounds {@link Producer} and {@link Consumer} + */ +public interface MessageQueue extends MqPublishPoint, MqSubscribePoint { + +} diff --git a/poison-pill/src/main/java/com/iluwatar/poison/pill/MqPublishPoint.java b/poison-pill/src/main/java/com/iluwatar/poison/pill/MqPublishPoint.java new file mode 100644 index 000000000..68dabf39c --- /dev/null +++ b/poison-pill/src/main/java/com/iluwatar/poison/pill/MqPublishPoint.java @@ -0,0 +1,31 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.poison.pill; + +/** + * Endpoint to publish {@link Message} to queue + */ +public interface MqPublishPoint { + + void put(Message msg) throws InterruptedException; +} diff --git a/poison-pill/src/main/java/com/iluwatar/poison/pill/MqSubscribePoint.java b/poison-pill/src/main/java/com/iluwatar/poison/pill/MqSubscribePoint.java new file mode 100644 index 000000000..8bc58226e --- /dev/null +++ b/poison-pill/src/main/java/com/iluwatar/poison/pill/MqSubscribePoint.java @@ -0,0 +1,31 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.poison.pill; + +/** + * Endpoint to retrieve {@link Message} from queue + */ +public interface MqSubscribePoint { + + Message take() throws InterruptedException; +} diff --git a/poison-pill/src/main/java/com/iluwatar/poison/pill/Producer.java b/poison-pill/src/main/java/com/iluwatar/poison/pill/Producer.java new file mode 100644 index 000000000..15dcad40e --- /dev/null +++ b/poison-pill/src/main/java/com/iluwatar/poison/pill/Producer.java @@ -0,0 +1,81 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.poison.pill; + +import java.util.Date; + +import com.iluwatar.poison.pill.Message.Headers; + +/** + * Class responsible for producing unit of work that can be expressed as message and submitted to + * queue + */ +public class Producer { + + private final MqPublishPoint queue; + private final String name; + private boolean isStopped; + + /** + * Constructor + */ + public Producer(String name, MqPublishPoint queue) { + this.name = name; + this.queue = queue; + this.isStopped = false; + } + + /** + * Send message to queue + */ + public void send(String body) { + if (isStopped) { + throw new IllegalStateException(String.format( + "Producer %s was stopped and fail to deliver requested message [%s].", body, name)); + } + Message msg = new SimpleMessage(); + msg.addHeader(Headers.DATE, new Date().toString()); + msg.addHeader(Headers.SENDER, name); + msg.setBody(body); + + try { + queue.put(msg); + } catch (InterruptedException e) { + // allow thread to exit + System.err.println(e); + } + } + + /** + * Stop system by sending poison pill + */ + public void stop() { + isStopped = true; + try { + queue.put(Message.POISON_PILL); + } catch (InterruptedException e) { + // allow thread to exit + System.err.println(e); + } + } +} diff --git a/poison-pill/src/main/java/com/iluwatar/poison/pill/SimpleMessage.java b/poison-pill/src/main/java/com/iluwatar/poison/pill/SimpleMessage.java new file mode 100644 index 000000000..3002449fd --- /dev/null +++ b/poison-pill/src/main/java/com/iluwatar/poison/pill/SimpleMessage.java @@ -0,0 +1,61 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.poison.pill; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * {@link Message} basic implementation + */ +public class SimpleMessage implements Message { + + private Map headers = new HashMap<>(); + private String body; + + @Override + public void addHeader(Headers header, String value) { + headers.put(header, value); + } + + @Override + public String getHeader(Headers header) { + return headers.get(header); + } + + @Override + public Map getHeaders() { + return Collections.unmodifiableMap(headers); + } + + @Override + public void setBody(String body) { + this.body = body; + } + + @Override + public String getBody() { + return body; + } +} diff --git a/poison-pill/src/main/java/com/iluwatar/poison/pill/SimpleMessageQueue.java b/poison-pill/src/main/java/com/iluwatar/poison/pill/SimpleMessageQueue.java new file mode 100644 index 000000000..6f45742e9 --- /dev/null +++ b/poison-pill/src/main/java/com/iluwatar/poison/pill/SimpleMessageQueue.java @@ -0,0 +1,48 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.poison.pill; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; + +/** + * Bounded blocking queue wrapper + */ +public class SimpleMessageQueue implements MessageQueue { + + private final BlockingQueue queue; + + public SimpleMessageQueue(int bound) { + queue = new ArrayBlockingQueue<>(bound); + } + + @Override + public void put(Message msg) throws InterruptedException { + queue.put(msg); + } + + @Override + public Message take() throws InterruptedException { + return queue.take(); + } +} diff --git a/poison-pill/src/main/java/com/iluwatar/poisonpill/App.java b/poison-pill/src/main/java/com/iluwatar/poisonpill/App.java deleted file mode 100644 index 85ed7c48e..000000000 --- a/poison-pill/src/main/java/com/iluwatar/poisonpill/App.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.iluwatar.poisonpill; - -/** - * One of possible approaches to terminate Producer-Consumer pattern is using PoisonPill idiom. - * If you use PoisonPill as termination signal then Producer is responsible to notify Consumer that exchange is over - * and reject any further messages. Consumer receiving PoisonPill will stop to read messages from queue. - * You also must ensure that PoisonPill will be last message that will be read from queue (if you have - * prioritized queue than this can be tricky). - * In simple cases as PoisonPill can be used just null-reference, but holding unique separate shared - * object-marker (with name "Poison" or "PoisonPill") is more clear and self describing. - */ -public class App { - - public static void main(String[] args) { - MessageQueue queue = new SimpleMessageQueue(10000); - - final Producer producer = new Producer("PRODUCER_1", queue); - final Consumer consumer = new Consumer("CONSUMER_1", queue); - - new Thread() { - @Override - public void run() { - consumer.consume(); - } - }.start(); - - new Thread() { - @Override - public void run() { - producer.send("hand shake"); - producer.send("some very important information"); - producer.send("bye!"); - producer.stop(); - } - }.start(); - } -} diff --git a/poison-pill/src/main/java/com/iluwatar/poisonpill/Consumer.java b/poison-pill/src/main/java/com/iluwatar/poisonpill/Consumer.java deleted file mode 100644 index d2892f502..000000000 --- a/poison-pill/src/main/java/com/iluwatar/poisonpill/Consumer.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.iluwatar.poisonpill; - -import com.iluwatar.poisonpill.Message.Headers; - -/** - * Class responsible for receiving and handling submitted to the queue messages - */ -public class Consumer { - - private final MQSubscribePoint queue; - private final String name; - - public Consumer(String name, MQSubscribePoint queue) { - this.name = name; - this.queue = queue; - } - - public void consume() { - while (true) { - Message msg; - try { - msg = queue.take(); - if (msg == Message.POISON_PILL) { - System.out.println(String.format("Consumer %s receive request to terminate.", name)); - break; - } - } catch (InterruptedException e) { - // allow thread to exit - System.err.println(e); - return; - } - - String sender = msg.getHeader(Headers.SENDER); - String body = msg.getBody(); - System.out.println(String.format("Message [%s] from [%s] received by [%s]", body, sender, name)); - } - } -} diff --git a/poison-pill/src/main/java/com/iluwatar/poisonpill/MQPublishPoint.java b/poison-pill/src/main/java/com/iluwatar/poisonpill/MQPublishPoint.java deleted file mode 100644 index ca3555f52..000000000 --- a/poison-pill/src/main/java/com/iluwatar/poisonpill/MQPublishPoint.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.iluwatar.poisonpill; - -/** - * Endpoint to publish {@link Message} to queue - */ -public interface MQPublishPoint { - - public void put(Message msg) throws InterruptedException; -} diff --git a/poison-pill/src/main/java/com/iluwatar/poisonpill/MQSubscribePoint.java b/poison-pill/src/main/java/com/iluwatar/poisonpill/MQSubscribePoint.java deleted file mode 100644 index 820d82bf3..000000000 --- a/poison-pill/src/main/java/com/iluwatar/poisonpill/MQSubscribePoint.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.iluwatar.poisonpill; - -/** - * Endpoint to retrieve {@link Message} from queue - */ -public interface MQSubscribePoint { - - public Message take() throws InterruptedException; -} diff --git a/poison-pill/src/main/java/com/iluwatar/poisonpill/Message.java b/poison-pill/src/main/java/com/iluwatar/poisonpill/Message.java deleted file mode 100644 index 1ea6c2904..000000000 --- a/poison-pill/src/main/java/com/iluwatar/poisonpill/Message.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.iluwatar.poisonpill; - -import java.util.Map; - -/** - * Interface that implements the Message pattern and represents an inbound or outbound message as part of an {@link Producer}-{@link Consumer} exchange. - */ -public interface Message { - - public static final Message POISON_PILL = new Message() { - - @Override - public void addHeader(Headers header, String value) { - throw poison(); - } - - @Override - public String getHeader(Headers header) { - throw poison(); - } - - @Override - public Map getHeaders() { - throw poison(); - } - - @Override - public void setBody(String body) { - throw poison(); - } - - @Override - public String getBody() { - throw poison(); - } - - private RuntimeException poison() { - return new UnsupportedOperationException("Poison"); - } - - }; - - public enum Headers { - DATE, SENDER - } - - public void addHeader(Headers header, String value); - public String getHeader(Headers header); - public Map getHeaders(); - public void setBody(String body); - public String getBody(); -} diff --git a/poison-pill/src/main/java/com/iluwatar/poisonpill/MessageQueue.java b/poison-pill/src/main/java/com/iluwatar/poisonpill/MessageQueue.java deleted file mode 100644 index 3a360cc5c..000000000 --- a/poison-pill/src/main/java/com/iluwatar/poisonpill/MessageQueue.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.iluwatar.poisonpill; - -/** - * Represents abstraction of channel (or pipe) that bounds {@link Producer} and {@link Consumer} - */ -public interface MessageQueue extends MQPublishPoint, MQSubscribePoint { - -} diff --git a/poison-pill/src/main/java/com/iluwatar/poisonpill/Producer.java b/poison-pill/src/main/java/com/iluwatar/poisonpill/Producer.java deleted file mode 100644 index 1e4bd9aa3..000000000 --- a/poison-pill/src/main/java/com/iluwatar/poisonpill/Producer.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.iluwatar.poisonpill; - -import java.util.Date; - -import com.iluwatar.poisonpill.Message.Headers; - -/** - * Class responsible for producing unit of work that can be expressed as message and submitted to queue - */ -public class Producer { - - private final MQPublishPoint queue; - private final String name; - private boolean isStopped; - - public Producer(String name, MQPublishPoint queue) { - this.name = name; - this.queue = queue; - this.isStopped = false; - } - - public void send(String body) { - if (isStopped) { - throw new IllegalStateException(String.format("Producer %s was stopped and fail to deliver requested message [%s].", body, name)); - } - Message msg = new SimpleMessage(); - msg.addHeader(Headers.DATE, new Date().toString()); - msg.addHeader(Headers.SENDER, name); - msg.setBody(body); - - try { - queue.put(msg); - } catch (InterruptedException e) { - // allow thread to exit - System.err.println(e); - } - } - - public void stop() { - isStopped = true; - try { - queue.put(Message.POISON_PILL); - } catch (InterruptedException e) { - // allow thread to exit - System.err.println(e); - } - } -} diff --git a/poison-pill/src/main/java/com/iluwatar/poisonpill/SimpleMessage.java b/poison-pill/src/main/java/com/iluwatar/poisonpill/SimpleMessage.java deleted file mode 100644 index 40c990712..000000000 --- a/poison-pill/src/main/java/com/iluwatar/poisonpill/SimpleMessage.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.iluwatar.poisonpill; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -/** - * {@link Message} basic implementation - */ -public class SimpleMessage implements Message { - - private Map headers = new HashMap<>(); - private String body; - - @Override - public void addHeader(Headers header, String value) { - headers.put(header, value); - } - - @Override - public String getHeader(Headers header) { - return headers.get(header); - } - - @Override - public Map getHeaders() { - return Collections.unmodifiableMap(headers); - } - - @Override - public void setBody(String body) { - this.body = body; - } - - @Override - public String getBody() { - return body; - } -} diff --git a/poison-pill/src/main/java/com/iluwatar/poisonpill/SimpleMessageQueue.java b/poison-pill/src/main/java/com/iluwatar/poisonpill/SimpleMessageQueue.java deleted file mode 100644 index cf012d9c4..000000000 --- a/poison-pill/src/main/java/com/iluwatar/poisonpill/SimpleMessageQueue.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.iluwatar.poisonpill; - -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; - -/** - * Bounded blocking queue wrapper - */ -public class SimpleMessageQueue implements MessageQueue { - - private final BlockingQueue queue; - - public SimpleMessageQueue(int bound) { - queue = new ArrayBlockingQueue(bound); - } - - @Override - public void put(Message msg) throws InterruptedException { - queue.put(msg); - } - - @Override - public Message take() throws InterruptedException { - return queue.take(); - } - -} diff --git a/poison-pill/src/test/java/com/iluwatar/poison/pill/AppTest.java b/poison-pill/src/test/java/com/iluwatar/poison/pill/AppTest.java new file mode 100644 index 000000000..5d1494004 --- /dev/null +++ b/poison-pill/src/test/java/com/iluwatar/poison/pill/AppTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.poison.pill; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/poison-pill/src/test/java/com/iluwatar/poison/pill/ConsumerTest.java b/poison-pill/src/test/java/com/iluwatar/poison/pill/ConsumerTest.java new file mode 100644 index 000000000..01a5dfaa6 --- /dev/null +++ b/poison-pill/src/test/java/com/iluwatar/poison/pill/ConsumerTest.java @@ -0,0 +1,78 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.poison.pill; + +import org.junit.Test; +import org.mockito.InOrder; + +import java.time.LocalDateTime; + +import static org.mockito.Mockito.inOrder; + +/** + * Date: 12/27/15 - 9:45 PM + * + * @author Jeroen Meulemeester + */ +public class ConsumerTest extends StdOutTest { + + @Test + public void testConsume() throws Exception { + final Message[] messages = new Message[]{ + createMessage("you", "Hello!"), + createMessage("me", "Hi!"), + Message.POISON_PILL, + createMessage("late_for_the_party", "Hello? Anyone here?"), + }; + + final MessageQueue queue = new SimpleMessageQueue(messages.length); + for (final Message message : messages) { + queue.put(message); + } + + new Consumer("NSA", queue).consume(); + + final InOrder inOrder = inOrder(getStdOutMock()); + inOrder.verify(getStdOutMock()).println("Message [Hello!] from [you] received by [NSA]"); + inOrder.verify(getStdOutMock()).println("Message [Hi!] from [me] received by [NSA]"); + inOrder.verify(getStdOutMock()).println("Consumer NSA receive request to terminate."); + inOrder.verifyNoMoreInteractions(); + + } + + /** + * Create a new message from the given sender with the given message body + * + * @param sender The sender's name + * @param message The message body + * @return The message instance + */ + private static Message createMessage(final String sender, final String message) { + final SimpleMessage msg = new SimpleMessage(); + msg.addHeader(Message.Headers.SENDER, sender); + msg.addHeader(Message.Headers.DATE, LocalDateTime.now().toString()); + msg.setBody(message); + return msg; + } + +} \ No newline at end of file diff --git a/poison-pill/src/test/java/com/iluwatar/poison/pill/PoisonMessageTest.java b/poison-pill/src/test/java/com/iluwatar/poison/pill/PoisonMessageTest.java new file mode 100644 index 000000000..4593c0822 --- /dev/null +++ b/poison-pill/src/test/java/com/iluwatar/poison/pill/PoisonMessageTest.java @@ -0,0 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.poison.pill; + +import org.junit.Test; + +import static com.iluwatar.poison.pill.Message.Headers; +import static com.iluwatar.poison.pill.Message.POISON_PILL; + +/** + * Date: 12/27/15 - 10:30 PM + * + * @author Jeroen Meulemeester + */ +public class PoisonMessageTest { + + @Test(expected = UnsupportedOperationException.class) + public void testAddHeader() throws Exception { + POISON_PILL.addHeader(Headers.SENDER, "sender"); + } + + @Test(expected = UnsupportedOperationException.class) + public void testGetHeader() throws Exception { + POISON_PILL.getHeader(Headers.SENDER); + } + + @Test(expected = UnsupportedOperationException.class) + public void testGetHeaders() throws Exception { + POISON_PILL.getHeaders(); + } + + @Test(expected = UnsupportedOperationException.class) + public void testSetBody() throws Exception { + POISON_PILL.setBody("Test message."); + } + + @Test(expected = UnsupportedOperationException.class) + public void testGetBody() throws Exception { + POISON_PILL.getBody(); + } + +} diff --git a/poison-pill/src/test/java/com/iluwatar/poison/pill/ProducerTest.java b/poison-pill/src/test/java/com/iluwatar/poison/pill/ProducerTest.java new file mode 100644 index 000000000..eec79edd1 --- /dev/null +++ b/poison-pill/src/test/java/com/iluwatar/poison/pill/ProducerTest.java @@ -0,0 +1,86 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.poison.pill; + +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/27/15 - 10:32 PM + * + * @author Jeroen Meulemeester + */ +public class ProducerTest { + + @Test + public void testSend() throws Exception { + final MqPublishPoint publishPoint = mock(MqPublishPoint.class); + final Producer producer = new Producer("producer", publishPoint); + verifyZeroInteractions(publishPoint); + + producer.send("Hello!"); + + final ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + verify(publishPoint).put(messageCaptor.capture()); + + final Message message = messageCaptor.getValue(); + assertNotNull(message); + assertEquals("producer", message.getHeader(Message.Headers.SENDER)); + assertNotNull(message.getHeader(Message.Headers.DATE)); + assertEquals("Hello!", message.getBody()); + + verifyNoMoreInteractions(publishPoint); + } + + @Test + public void testStop() throws Exception { + final MqPublishPoint publishPoint = mock(MqPublishPoint.class); + final Producer producer = new Producer("producer", publishPoint); + verifyZeroInteractions(publishPoint); + + producer.stop(); + verify(publishPoint).put(eq(Message.POISON_PILL)); + + try { + producer.send("Hello!"); + fail("Expected 'IllegalStateException' at this point, since the producer has stopped!"); + } catch (IllegalStateException e) { + assertNotNull(e); + assertNotNull(e.getMessage()); + assertEquals("Producer Hello! was stopped and fail to deliver requested message [producer].", + e.getMessage()); + } + + verifyNoMoreInteractions(publishPoint); + } + +} diff --git a/poison-pill/src/test/java/com/iluwatar/poison/pill/SimpleMessageTest.java b/poison-pill/src/test/java/com/iluwatar/poison/pill/SimpleMessageTest.java new file mode 100644 index 000000000..0fc4ef036 --- /dev/null +++ b/poison-pill/src/test/java/com/iluwatar/poison/pill/SimpleMessageTest.java @@ -0,0 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.poison.pill; + +import org.junit.Test; + +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Date: 12/27/15 - 10:25 PM + * + * @author Jeroen Meulemeester + */ +public class SimpleMessageTest { + + @Test + public void testGetHeaders() { + final SimpleMessage message = new SimpleMessage(); + assertNotNull(message.getHeaders()); + assertTrue(message.getHeaders().isEmpty()); + + final String senderName = "test"; + message.addHeader(Message.Headers.SENDER, senderName); + assertNotNull(message.getHeaders()); + assertFalse(message.getHeaders().isEmpty()); + assertEquals(senderName, message.getHeaders().get(Message.Headers.SENDER)); + } + + @Test(expected = UnsupportedOperationException.class) + public void testUnModifiableHeaders() { + final SimpleMessage message = new SimpleMessage(); + final Map headers = message.getHeaders(); + headers.put(Message.Headers.SENDER, "test"); + } + + +} \ No newline at end of file diff --git a/poison-pill/src/test/java/com/iluwatar/poison/pill/StdOutTest.java b/poison-pill/src/test/java/com/iluwatar/poison/pill/StdOutTest.java new file mode 100644 index 000000000..f1b3c4840 --- /dev/null +++ b/poison-pill/src/test/java/com/iluwatar/poison/pill/StdOutTest.java @@ -0,0 +1,75 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.poison.pill; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since some actions don't have any + * influence on accessible objects, except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/poison-pill/src/test/java/com/iluwatar/poisonpill/AppTest.java b/poison-pill/src/test/java/com/iluwatar/poisonpill/AppTest.java deleted file mode 100644 index 6d39c06bb..000000000 --- a/poison-pill/src/test/java/com/iluwatar/poisonpill/AppTest.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.iluwatar.poisonpill; - -import org.junit.Test; - -import com.iluwatar.poisonpill.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} diff --git a/pom.xml b/pom.xml index 3d5dbfc91..6f3e0d698 100644 --- a/pom.xml +++ b/pom.xml @@ -1,21 +1,52 @@ - + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT pom + 2014 + UTF-8 - 5.0.0.Beta1 - 1.4.187 + 5.0.1.Final + 4.2.4.RELEASE + 1.3.3.RELEASE + 1.9.2.RELEASE + 1.4.190 4.12 3.0 - 3.1.0 + 4.0.0 0.7.2.201409121644 + 1.4 + 2.16.1 + 1.2.17 + 19.0 + 1.15.1 + 1.10.19 + 4.12.1 + 4.5.2 + 2.22 abstract-factory @@ -27,6 +58,7 @@ bridge composite dao + data-mapper decorator facade flyweight @@ -52,7 +84,9 @@ execute-around property intercepting-filter + producer-consumer poison-pill + reader-writer-lock lazy-loading service-layer specification @@ -63,9 +97,38 @@ multiton resource-acquisition-is-initialization thread-pool + twin private-class-data object-pool - dependency-injection + dependency-injection + naked-objects + front-controller + repository + async-method-invocation + monostate + step-builder + business-delegate + half-sync-half-async + layers + message-channel + fluentinterface + reactor + caching + publish-subscribe + delegation + event-driven-architecture + api-gateway + factory-kit + feature-toggle + value-object + monad + mute-idiom + mutex + semaphore + hexagonal + abstract-document + aggregator-microservices + page-object @@ -75,57 +138,141 @@ hibernate-core ${hibernate.version} + + org.hibernate + hibernate-entitymanager + ${hibernate.version} + + + org.springframework + spring-test + ${spring.version} + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + org.springframework.data + spring-data-jpa + ${spring-data.version} + + + org.springframework + spring-webmvc + ${spring.version} + + + org.springframework.boot + spring-boot-starter-web + ${spring-boot.version} + + + org.apache.httpcomponents + httpclient + ${apache-httpcomponents.version} + com.h2database h2 ${h2.version} + + commons-dbcp + commons-dbcp + ${commons-dbcp.version} + + + org.apache.camel + camel-core + ${camel.version} + + + org.apache.camel + camel-stream + ${camel.version} + junit junit ${junit.version} test + + org.mockito + mockito-core + ${mockito.version} + test + + + log4j + log4j + ${log4j.version} + + + com.google.guava + guava + ${guava.version} + + + com.github.stefanbirkner + system-rules + ${systemrules.version} + test + + + de.bechte.junit + junit-hierarchicalcontextrunner + ${hierarchical-junit-runner-version} + test + + + net.sourceforge.htmlunit + htmlunit + ${htmlunit.version} + test + - + - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - org.jacoco - - jacoco-maven-plugin - - - [0.6.2,) - - - prepare-agent - - - - - - - - - - + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.jacoco + + jacoco-maven-plugin + + + [0.6.2,) + + + prepare-agent + + + + + + + + + + @@ -141,27 +288,151 @@ - org.eluder.coveralls - coveralls-maven-plugin - ${coveralls.version} - - jb6wYzxkVvjolD6qOWpzWdcWBzYk2fAmF - - + org.eluder.coveralls + coveralls-maven-plugin + ${coveralls.version} + + jb6wYzxkVvjolD6qOWpzWdcWBzYk2fAmF + + - org.jacoco - jacoco-maven-plugin - ${jacoco.version} - - - prepare-agent - - prepare-agent - - - - + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + + + domainapp/dom/modules/simple/QSimpleObject.class + **com.steadystate* + + + + + prepare-agent + + prepare-agent + + + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 2.17 + + + validate + + check + + validate + + checkstyle.xml + checkstyle-suppressions.xml + UTF-8 + true + true + true + + + + + + + org.jacoco + jacoco-maven-plugin + 0.7.5.201505241946 + + + + prepare-agent + + + + report + prepare-package + + report + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18.1 + + + org.apache.maven.surefire + surefire-junit47 + 2.18.1 + + + + -Xmx1024M ${argLine} + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.6 + + true + 5 + true + + + + + check + + + exclude-pmd.properties + + + + + + + com.mycila + license-maven-plugin + 2.11 + +

com/mycila/maven/plugin/license/templates/MIT.txt
+ + Ilkka Seppälä + + true + + + + install-format + install + + format + + + + + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.6 + + + +
diff --git a/private-class-data/README.md b/private-class-data/README.md new file mode 100644 index 000000000..981208fa3 --- /dev/null +++ b/private-class-data/README.md @@ -0,0 +1,23 @@ +--- +layout: pattern +title: Private Class Data +folder: private-class-data +permalink: /patterns/private-class-data/ +categories: Other +tags: + - Java + - Difficulty-Beginner + - Idiom +--- + +## Intent +Private Class Data design pattern seeks to reduce exposure of +attributes by limiting their visibility. It reduces the number of class +attributes by encapsulating them in single Data object. + +![alt text](./etc/private-class-data.png "Private Class Data") + +## Applicability +Use the Private Class Data pattern when + +* you want to prevent write access to class data members diff --git a/private-class-data/pom.xml b/private-class-data/pom.xml index 58fdeef04..20484245b 100644 --- a/private-class-data/pom.xml +++ b/private-class-data/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT private-class-data @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/private-class-data/src/main/java/com/iluwatar/privateclassdata/App.java b/private-class-data/src/main/java/com/iluwatar/privateclassdata/App.java index 8cc5254d6..7a58fc6f3 100644 --- a/private-class-data/src/main/java/com/iluwatar/privateclassdata/App.java +++ b/private-class-data/src/main/java/com/iluwatar/privateclassdata/App.java @@ -1,34 +1,59 @@ -package com.iluwatar.privateclassdata; - -/** - * - * The Private Class Data design pattern seeks to reduce exposure of attributes - * by limiting their visibility. It reduces the number of class attributes by - * encapsulating them in single data object. It allows the class designer to - * remove write privilege of attributes that are intended to be set only during - * construction, even from methods of the target class. - * - * In the example we have normal Stew class with some ingredients given in - * constructor. Then we have methods to enumerate the ingredients and to taste - * the stew. The method for tasting the stew alters the private members of the - * stew class. - * - * The problem is solved with the Private Class Data pattern. We introduce - * ImmutableStew class that contains StewData. The private data members of - * Stew are now in StewData and cannot be altered by ImmutableStew methods. - * - */ -public class App { - - public static void main( String[] args ) { - // stew is mutable - Stew stew = new Stew(1, 2, 3, 4); - stew.mix(); - stew.taste(); - stew.mix(); - - // immutable stew protected with Private Class Data pattern - ImmutableStew immutableStew = new ImmutableStew(2, 4, 3, 6); - immutableStew.mix(); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.privateclassdata; + +/** + * + * The Private Class Data design pattern seeks to reduce exposure of attributes by limiting their + * visibility. It reduces the number of class attributes by encapsulating them in single data + * object. It allows the class designer to remove write privilege of attributes that are intended to + * be set only during construction, even from methods of the target class. + *

+ * In the example we have normal {@link Stew} class with some ingredients given in constructor. Then + * we have methods to enumerate the ingredients and to taste the stew. The method for tasting the + * stew alters the private members of the {@link Stew} class. + * + * The problem is solved with the Private Class Data pattern. We introduce {@link ImmutableStew} + * class that contains {@link StewData}. The private data members of {@link Stew} are now in + * {@link StewData} and cannot be altered by {@link ImmutableStew} methods. + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + // stew is mutable + Stew stew = new Stew(1, 2, 3, 4); + stew.mix(); + stew.taste(); + stew.mix(); + + // immutable stew protected with Private Class Data pattern + ImmutableStew immutableStew = new ImmutableStew(2, 4, 3, 6); + immutableStew.mix(); + } +} diff --git a/private-class-data/src/main/java/com/iluwatar/privateclassdata/ImmutableStew.java b/private-class-data/src/main/java/com/iluwatar/privateclassdata/ImmutableStew.java index ebaaf28a0..b9d2ad09c 100644 --- a/private-class-data/src/main/java/com/iluwatar/privateclassdata/ImmutableStew.java +++ b/private-class-data/src/main/java/com/iluwatar/privateclassdata/ImmutableStew.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.privateclassdata; /** @@ -7,14 +29,18 @@ package com.iluwatar.privateclassdata; */ public class ImmutableStew { - private StewData data; - - public ImmutableStew(int numPotatoes, int numCarrots, int numMeat, int numPeppers) { - data = new StewData(numPotatoes, numCarrots, numMeat, numPeppers); - } - - public void mix() { - System.out.println(String.format("Mixing the immutable stew we find: %d potatoes, %d carrots, %d meat and %d peppers", - data.getNumPotatoes(), data.getNumCarrots(), data.getNumMeat(), data.getNumPeppers())); - } + private StewData data; + + public ImmutableStew(int numPotatoes, int numCarrots, int numMeat, int numPeppers) { + data = new StewData(numPotatoes, numCarrots, numMeat, numPeppers); + } + + /** + * Mix the stew + */ + public void mix() { + System.out.println(String.format( + "Mixing the immutable stew we find: %d potatoes, %d carrots, %d meat and %d peppers", + data.getNumPotatoes(), data.getNumCarrots(), data.getNumMeat(), data.getNumPeppers())); + } } diff --git a/private-class-data/src/main/java/com/iluwatar/privateclassdata/Stew.java b/private-class-data/src/main/java/com/iluwatar/privateclassdata/Stew.java index 6ad05044b..9c9033018 100644 --- a/private-class-data/src/main/java/com/iluwatar/privateclassdata/Stew.java +++ b/private-class-data/src/main/java/com/iluwatar/privateclassdata/Stew.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.privateclassdata; /** @@ -6,37 +28,47 @@ package com.iluwatar.privateclassdata; * */ public class Stew { - - private int numPotatoes; - private int numCarrots; - private int numMeat; - private int numPeppers; - - public Stew(int numPotatoes, int numCarrots, int numMeat, int numPeppers) { - this.numPotatoes = numPotatoes; - this.numCarrots = numCarrots; - this.numMeat = numMeat; - this.numPeppers = numPeppers; - } - - public void mix() { - System.out.println(String.format("Mixing the stew we find: %d potatoes, %d carrots, %d meat and %d peppers", - numPotatoes, numCarrots, numMeat, numPeppers)); - } - - public void taste() { - System.out.println("Tasting the stew"); - if (numPotatoes > 0) { - numPotatoes--; - } - if (numCarrots > 0) { - numCarrots--; - } - if (numMeat > 0) { - numMeat--; - } - if (numPeppers > 0) { - numPeppers--; - } - } + + private int numPotatoes; + private int numCarrots; + private int numMeat; + private int numPeppers; + + /** + * Constructor + */ + public Stew(int numPotatoes, int numCarrots, int numMeat, int numPeppers) { + this.numPotatoes = numPotatoes; + this.numCarrots = numCarrots; + this.numMeat = numMeat; + this.numPeppers = numPeppers; + } + + /** + * Mix the stew + */ + public void mix() { + System.out.println(String.format( + "Mixing the stew we find: %d potatoes, %d carrots, %d meat and %d peppers", numPotatoes, + numCarrots, numMeat, numPeppers)); + } + + /** + * Taste the stew + */ + public void taste() { + System.out.println("Tasting the stew"); + if (numPotatoes > 0) { + numPotatoes--; + } + if (numCarrots > 0) { + numCarrots--; + } + if (numMeat > 0) { + numMeat--; + } + if (numPeppers > 0) { + numPeppers--; + } + } } diff --git a/private-class-data/src/main/java/com/iluwatar/privateclassdata/StewData.java b/private-class-data/src/main/java/com/iluwatar/privateclassdata/StewData.java index f90d3bd18..06c0b8c39 100644 --- a/private-class-data/src/main/java/com/iluwatar/privateclassdata/StewData.java +++ b/private-class-data/src/main/java/com/iluwatar/privateclassdata/StewData.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.privateclassdata; /** @@ -7,31 +29,34 @@ package com.iluwatar.privateclassdata; */ public class StewData { - private int numPotatoes; - private int numCarrots; - private int numMeat; - private int numPeppers; - - public StewData(int numPotatoes, int numCarrots, int numMeat, int numPeppers) { - this.numPotatoes = numPotatoes; - this.numCarrots = numCarrots; - this.numMeat = numMeat; - this.numPeppers = numPeppers; - } + private int numPotatoes; + private int numCarrots; + private int numMeat; + private int numPeppers; - public int getNumPotatoes() { - return numPotatoes; - } + /** + * Constructor + */ + public StewData(int numPotatoes, int numCarrots, int numMeat, int numPeppers) { + this.numPotatoes = numPotatoes; + this.numCarrots = numCarrots; + this.numMeat = numMeat; + this.numPeppers = numPeppers; + } - public int getNumCarrots() { - return numCarrots; - } + public int getNumPotatoes() { + return numPotatoes; + } - public int getNumMeat() { - return numMeat; - } + public int getNumCarrots() { + return numCarrots; + } - public int getNumPeppers() { - return numPeppers; - } + public int getNumMeat() { + return numMeat; + } + + public int getNumPeppers() { + return numPeppers; + } } diff --git a/private-class-data/src/test/java/com/iluwatar/privateclassdata/AppTest.java b/private-class-data/src/test/java/com/iluwatar/privateclassdata/AppTest.java index 8ba8c4b80..2625c4906 100644 --- a/private-class-data/src/test/java/com/iluwatar/privateclassdata/AppTest.java +++ b/private-class-data/src/test/java/com/iluwatar/privateclassdata/AppTest.java @@ -1,14 +1,39 @@ -package com.iluwatar.privateclassdata; - -import org.junit.Test; - -import com.iluwatar.privateclassdata.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.privateclassdata; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/private-class-data/src/test/java/com/iluwatar/privateclassdata/ImmutableStewTest.java b/private-class-data/src/test/java/com/iluwatar/privateclassdata/ImmutableStewTest.java new file mode 100644 index 000000000..58867d303 --- /dev/null +++ b/private-class-data/src/test/java/com/iluwatar/privateclassdata/ImmutableStewTest.java @@ -0,0 +1,74 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.privateclassdata; + +import org.junit.Test; +import org.mockito.InOrder; + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.verify; + +/** + * Date: 12/27/15 - 10:46 PM + * + * @author Jeroen Meulemeester + */ +public class ImmutableStewTest extends StdOutTest { + + /** + * Verify if mixing the stew doesn't change the internal state + */ + @Test + public void testMix() { + final Stew stew = new Stew(1, 2, 3, 4); + final String message = "Mixing the stew we find: 1 potatoes, 2 carrots, 3 meat and 4 peppers"; + + final InOrder inOrder = inOrder(getStdOutMock()); + for (int i = 0; i < 20; i++) { + stew.mix(); + inOrder.verify(getStdOutMock()).println(message); + } + + inOrder.verifyNoMoreInteractions(); + } + + /** + * Verify if tasting the stew actually removes one of each ingredient + */ + @Test + public void testDrink() { + final Stew stew = new Stew(1, 2, 3, 4); + stew.mix(); + + verify(getStdOutMock()) + .println("Mixing the stew we find: 1 potatoes, 2 carrots, 3 meat and 4 peppers"); + + stew.taste(); + verify(getStdOutMock()).println("Tasting the stew"); + + stew.mix(); + verify(getStdOutMock()) + .println("Mixing the stew we find: 0 potatoes, 1 carrots, 2 meat and 3 peppers"); + + } +} \ No newline at end of file diff --git a/private-class-data/src/test/java/com/iluwatar/privateclassdata/StdOutTest.java b/private-class-data/src/test/java/com/iluwatar/privateclassdata/StdOutTest.java new file mode 100644 index 000000000..f90551020 --- /dev/null +++ b/private-class-data/src/test/java/com/iluwatar/privateclassdata/StdOutTest.java @@ -0,0 +1,75 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.privateclassdata; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since some actions don't have any + * influence on accessible objects, except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/private-class-data/src/test/java/com/iluwatar/privateclassdata/StewTest.java b/private-class-data/src/test/java/com/iluwatar/privateclassdata/StewTest.java new file mode 100644 index 000000000..a894e4ae0 --- /dev/null +++ b/private-class-data/src/test/java/com/iluwatar/privateclassdata/StewTest.java @@ -0,0 +1,55 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.privateclassdata; + +import org.junit.Test; +import org.mockito.InOrder; + +import static org.mockito.Mockito.inOrder; + +/** + * Date: 12/27/15 - 10:46 PM + * + * @author Jeroen Meulemeester + */ +public class StewTest extends StdOutTest { + + /** + * Verify if mixing the stew doesn't change the internal state + */ + @Test + public void testMix() { + final ImmutableStew stew = new ImmutableStew(1, 2, 3, 4); + final String expectedMessage = "Mixing the immutable stew we find: 1 potatoes, " + + "2 carrots, 3 meat and 4 peppers"; + + final InOrder inOrder = inOrder(getStdOutMock()); + for (int i = 0; i < 20; i++) { + stew.mix(); + inOrder.verify(getStdOutMock()).println(expectedMessage); + } + + inOrder.verifyNoMoreInteractions(); + } + +} \ No newline at end of file diff --git a/producer-consumer/README.md b/producer-consumer/README.md new file mode 100644 index 000000000..1bb84c35f --- /dev/null +++ b/producer-consumer/README.md @@ -0,0 +1,25 @@ +--- +layout: pattern +title: Producer Consumer +folder: producer-consumer +permalink: /patterns/producer-consumer/ +categories: Concurrency +tags: + - Java + - Difficulty-Intermediate + - I/O + - Reactive +--- + +## Intent +Producer Consumer Design pattern is a classic concurrency pattern which reduces + coupling between Producer and Consumer by separating Identification of work with Execution of + Work. + +![alt text](./etc/producer-consumer.png "Producer Consumer") + +## Applicability +Use the Producer Consumer idiom when + +* decouple system by separate work in two process produce and consume. +* addresses the issue of different timing require to produce work or consuming work diff --git a/producer-consumer/etc/producer-consumer.png b/producer-consumer/etc/producer-consumer.png new file mode 100644 index 000000000..e8bc573b3 Binary files /dev/null and b/producer-consumer/etc/producer-consumer.png differ diff --git a/producer-consumer/etc/producer-consumer.ucls b/producer-consumer/etc/producer-consumer.ucls new file mode 100644 index 000000000..aa1ee80c0 --- /dev/null +++ b/producer-consumer/etc/producer-consumer.ucls @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/producer-consumer/pom.xml b/producer-consumer/pom.xml new file mode 100644 index 000000000..a72aa2c96 --- /dev/null +++ b/producer-consumer/pom.xml @@ -0,0 +1,47 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + producer-consumer + + + junit + junit + test + + + org.mockito + mockito-core + test + + + diff --git a/producer-consumer/src/main/java/com/iluwatar/producer/consumer/App.java b/producer-consumer/src/main/java/com/iluwatar/producer/consumer/App.java new file mode 100644 index 000000000..42f6dfa79 --- /dev/null +++ b/producer-consumer/src/main/java/com/iluwatar/producer/consumer/App.java @@ -0,0 +1,78 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.producer.consumer; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * Producer Consumer Design pattern is a classic concurrency or threading pattern which reduces coupling between + * Producer and Consumer by separating Identification of work with Execution of Work. + *

+ * In producer consumer design pattern a shared queue is used to control the flow and this separation allows you to code + * producer and consumer separately. It also addresses the issue of different timing require to produce item or + * consuming item. by using producer consumer pattern both Producer and Consumer Thread can work with different speed. + * + */ +public class App { + + /** + * Program entry point + * + * @param args + * command line args + */ + public static void main(String[] args) { + + ItemQueue queue = new ItemQueue(); + + ExecutorService executorService = Executors.newFixedThreadPool(5); + for (int i = 0; i < 2; i++) { + + final Producer producer = new Producer("Producer_" + i, queue); + executorService.submit(() -> { + while (true) { + producer.produce(); + } + }); + } + + for (int i = 0; i < 3; i++) { + final Consumer consumer = new Consumer("Consumer_" + i, queue); + executorService.submit(() -> { + while (true) { + consumer.consume(); + } + }); + } + + executorService.shutdown(); + try { + executorService.awaitTermination(10, TimeUnit.SECONDS); + executorService.shutdownNow(); + } catch (InterruptedException e) { + System.out.println("Error waiting for ExecutorService shutdown"); + } + } +} diff --git a/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Consumer.java b/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Consumer.java new file mode 100644 index 000000000..f1fa920f3 --- /dev/null +++ b/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Consumer.java @@ -0,0 +1,49 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.producer.consumer; + +/** + * Class responsible for consume the {@link Item} produced by {@link Producer} + */ +public class Consumer { + + private final ItemQueue queue; + + private final String name; + + public Consumer(String name, ItemQueue queue) { + this.name = name; + this.queue = queue; + } + + /** + * Consume item from the queue + */ + public void consume() throws InterruptedException { + + Item item = queue.take(); + System.out.println(String.format("Consumer [%s] consume item [%s] produced by [%s]", name, + item.getId(), item.getProducer())); + + } +} diff --git a/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Item.java b/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Item.java new file mode 100644 index 000000000..4bf66e599 --- /dev/null +++ b/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Item.java @@ -0,0 +1,49 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.producer.consumer; + +/** + * Class take part of an {@link Producer}-{@link Consumer} exchange. + */ +public class Item { + + private String producer; + + private int id; + + public Item(String producer, int id) { + this.id = id; + this.producer = producer; + } + + public int getId() { + + return id; + } + + public String getProducer() { + + return producer; + } + +} diff --git a/producer-consumer/src/main/java/com/iluwatar/producer/consumer/ItemQueue.java b/producer-consumer/src/main/java/com/iluwatar/producer/consumer/ItemQueue.java new file mode 100644 index 000000000..ea925152b --- /dev/null +++ b/producer-consumer/src/main/java/com/iluwatar/producer/consumer/ItemQueue.java @@ -0,0 +1,50 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.producer.consumer; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +/** + * Class as a channel for {@link Producer}-{@link Consumer} exchange. + */ +public class ItemQueue { + + private BlockingQueue queue; + + public ItemQueue() { + + queue = new LinkedBlockingQueue<>(5); + } + + public void put(Item item) throws InterruptedException { + + queue.put(item); + } + + public Item take() throws InterruptedException { + + return queue.take(); + } + +} diff --git a/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Producer.java b/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Producer.java new file mode 100644 index 000000000..587614dc8 --- /dev/null +++ b/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Producer.java @@ -0,0 +1,54 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.producer.consumer; + +import java.util.Random; + +/** + * Class responsible for producing unit of work that can be expressed as {@link Item} and submitted + * to queue + */ +public class Producer { + + private final ItemQueue queue; + + private final String name; + + private int itemId; + + public Producer(String name, ItemQueue queue) { + this.name = name; + this.queue = queue; + } + + /** + * Put item in the queue + */ + public void produce() throws InterruptedException { + + Item item = new Item(name, itemId++); + queue.put(item); + Random random = new Random(); + Thread.sleep(random.nextInt(2000)); + } +} diff --git a/producer-consumer/src/test/java/com/iluwatar/producer/consumer/AppTest.java b/producer-consumer/src/test/java/com/iluwatar/producer/consumer/AppTest.java new file mode 100644 index 000000000..fcc6509a8 --- /dev/null +++ b/producer-consumer/src/test/java/com/iluwatar/producer/consumer/AppTest.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.producer.consumer; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() throws Exception { + String[] args = {}; + App.main(args); + + } +} diff --git a/producer-consumer/src/test/java/com/iluwatar/producer/consumer/ConsumerTest.java b/producer-consumer/src/test/java/com/iluwatar/producer/consumer/ConsumerTest.java new file mode 100644 index 000000000..2ca547a0b --- /dev/null +++ b/producer-consumer/src/test/java/com/iluwatar/producer/consumer/ConsumerTest.java @@ -0,0 +1,61 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.producer.consumer; + +import org.junit.Test; +import org.mockito.InOrder; + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; + +/** + * Date: 12/27/15 - 11:01 PM + * + * @author Jeroen Meulemeester + */ +public class ConsumerTest extends StdOutTest { + + private static final int ITEM_COUNT = 5; + + @Test + public void testConsume() throws Exception { + final ItemQueue queue = spy(new ItemQueue()); + for (int id = 0; id < ITEM_COUNT; id++) { + queue.put(new Item("producer", id)); + } + + reset(queue); // Don't count the preparation above as interactions with the queue + final Consumer consumer = new Consumer("consumer", queue); + + final InOrder inOrder = inOrder(getStdOutMock()); + for (int id = 0; id < ITEM_COUNT; id++) { + consumer.consume(); + inOrder.verify(getStdOutMock()) + .println("Consumer [consumer] consume item [" + id + "] produced by [producer]"); + } + + inOrder.verifyNoMoreInteractions(); + } + +} diff --git a/producer-consumer/src/test/java/com/iluwatar/producer/consumer/ProducerTest.java b/producer-consumer/src/test/java/com/iluwatar/producer/consumer/ProducerTest.java new file mode 100644 index 000000000..94765268b --- /dev/null +++ b/producer-consumer/src/test/java/com/iluwatar/producer/consumer/ProducerTest.java @@ -0,0 +1,50 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.producer.consumer; + +import org.junit.Test; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/28/15 - 12:12 AM + * + * @author Jeroen Meulemeester + */ +public class ProducerTest { + + @Test(timeout = 6000) + public void testProduce() throws Exception { + final ItemQueue queue = mock(ItemQueue.class); + final Producer producer = new Producer("producer", queue); + + producer.produce(); + verify(queue).put(any(Item.class)); + + verifyNoMoreInteractions(queue); + } + +} \ No newline at end of file diff --git a/producer-consumer/src/test/java/com/iluwatar/producer/consumer/StdOutTest.java b/producer-consumer/src/test/java/com/iluwatar/producer/consumer/StdOutTest.java new file mode 100644 index 000000000..1e1c41f75 --- /dev/null +++ b/producer-consumer/src/test/java/com/iluwatar/producer/consumer/StdOutTest.java @@ -0,0 +1,75 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.producer.consumer; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since some actions don't have any + * influence on accessible objects, except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/property/README.md b/property/README.md new file mode 100644 index 000000000..0ac5c7a6c --- /dev/null +++ b/property/README.md @@ -0,0 +1,25 @@ +--- +layout: pattern +title: Property +folder: property +permalink: /patterns/property/ +categories: Creational +tags: + - Java + - Difficulty-Beginner +--- + +## Intent +Create hierarchy of objects and new objects using already existing +objects as parents. + +![alt text](./etc/property.png "Property") + +## Applicability +Use the Property pattern when + +* when you like to have objects with dynamic set of fields and prototype inheritance + +## Real world examples + +* [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain) prototype inheritance diff --git a/property/pom.xml b/property/pom.xml index 579ea9041..1e9d0f0c2 100644 --- a/property/pom.xml +++ b/property/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT property diff --git a/property/src/main/java/com/iluwatar/property/App.java b/property/src/main/java/com/iluwatar/property/App.java index 40972ab59..e3f7cc92b 100644 --- a/property/src/main/java/com/iluwatar/property/App.java +++ b/property/src/main/java/com/iluwatar/property/App.java @@ -1,49 +1,82 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.property; import com.iluwatar.property.Character.Type; /** - * Example of Character instantiation using Property pattern (as concept also known like Prototype inheritance). - * In prototype inheritance instead of classes, as opposite to Java class inheritance, - * objects are used to create another objects and object hierarchies. - * Hierarchies are created using prototype chain through delegation: every object has link to parent object. - * Any base (parent) object can be amended at runtime (by adding or removal of some property), and all child objects will be affected as result. + * + * The Property pattern is also known as Prototype inheritance. + *

+ * In prototype inheritance instead of classes, as opposite to Java class inheritance, objects are + * used to create another objects and object hierarchies. Hierarchies are created using prototype + * chain through delegation: every object has link to parent object. Any base (parent) object can be + * amended at runtime (by adding or removal of some property), and all child objects will be + * affected as result. + *

+ * In this example we demonstrate {@link Character} instantiation using the Property pattern. + * */ public class App { - public static void main(String[] args) { - /* set up */ - Prototype charProto = new Character(); - charProto.set(Stats.STRENGTH, 10); - charProto.set(Stats.AGILITY, 10); - charProto.set(Stats.ARMOR, 10); - charProto.set(Stats.ATTACK_POWER, 10); + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + /* set up */ + Prototype charProto = new Character(); + charProto.set(Stats.STRENGTH, 10); + charProto.set(Stats.AGILITY, 10); + charProto.set(Stats.ARMOR, 10); + charProto.set(Stats.ATTACK_POWER, 10); - Character mageProto = new Character(Type.MAGE, charProto); - mageProto.set(Stats.INTELLECT, 15); - mageProto.set(Stats.SPIRIT, 10); + Character mageProto = new Character(Type.MAGE, charProto); + mageProto.set(Stats.INTELLECT, 15); + mageProto.set(Stats.SPIRIT, 10); - Character warProto = new Character(Type.WARRIOR, charProto); - warProto.set(Stats.RAGE, 15); - warProto.set(Stats.ARMOR, 15); // boost default armor for warrior + Character warProto = new Character(Type.WARRIOR, charProto); + warProto.set(Stats.RAGE, 15); + warProto.set(Stats.ARMOR, 15); // boost default armor for warrior - Character rogueProto = new Character(Type.ROGUE, charProto); - rogueProto.set(Stats.ENERGY, 15); - rogueProto.set(Stats.AGILITY, 15); // boost default agility for rogue + Character rogueProto = new Character(Type.ROGUE, charProto); + rogueProto.set(Stats.ENERGY, 15); + rogueProto.set(Stats.AGILITY, 15); // boost default agility for rogue - /* usage */ - Character mag = new Character("Player_1", mageProto); - mag.set(Stats.ARMOR, 8); - System.out.println(mag); + /* usage */ + Character mag = new Character("Player_1", mageProto); + mag.set(Stats.ARMOR, 8); + System.out.println(mag); - Character warrior = new Character("Player_2", warProto); - System.out.println(warrior); + Character warrior = new Character("Player_2", warProto); + System.out.println(warrior); - Character rogue = new Character("Player_3", rogueProto); - System.out.println(rogue); + Character rogue = new Character("Player_3", rogueProto); + System.out.println(rogue); - Character rogueDouble = new Character("Player_4", rogue); - rogueDouble.set(Stats.ATTACK_POWER, 12); - System.out.println(rogueDouble); - } + Character rogueDouble = new Character("Player_4", rogue); + rogueDouble.set(Stats.ATTACK_POWER, 12); + System.out.println(rogueDouble); + } } diff --git a/property/src/main/java/com/iluwatar/property/Character.java b/property/src/main/java/com/iluwatar/property/Character.java index cb2fdf583..6a45d16b2 100644 --- a/property/src/main/java/com/iluwatar/property/Character.java +++ b/property/src/main/java/com/iluwatar/property/Character.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.property; import java.util.HashMap; @@ -8,110 +30,106 @@ import java.util.Map; */ public class Character implements Prototype { - public enum Type { - WARRIOR, MAGE, ROGUE - } + public enum Type { + WARRIOR, MAGE, ROGUE + } - private final Prototype prototype; - private final Map properties = new HashMap<>(); + private final Prototype prototype; + private final Map properties = new HashMap<>(); - private String name; - private Type type; + private String name; + private Type type; - public Character() { - this.prototype = new Prototype() { // Null-value object - @Override - public Integer get(Stats stat) { - return null; - } - @Override - public boolean has(Stats stat) { - return false; - } - @Override - public void set(Stats stat, Integer val) { - } - @Override - public void remove(Stats stat) { - }} - ; - } + /** + * Constructor + */ + public Character() { + this.prototype = new Prototype() { // Null-value object + @Override + public Integer get(Stats stat) { + return null; + } - public Character(Type type, Prototype prototype) { - this.type = type; - this.prototype = prototype; - } + @Override + public boolean has(Stats stat) { + return false; + } - public Character(String name, Character prototype) { - this.name = name; - this.type = prototype.type; - this.prototype = prototype; - } + @Override + public void set(Stats stat, Integer val) {} - public String name() { - return name; - } + @Override + public void remove(Stats stat) {} + }; + } - public Type type() { - return type; - } + public Character(Type type, Prototype prototype) { + this.type = type; + this.prototype = prototype; + } - @Override - public Integer get(Stats stat) { - boolean containsValue = properties.containsKey(stat); - if (containsValue) { - return properties.get(stat); - } else { - return prototype.get(stat); - } - } + /** + * Constructor + */ + public Character(String name, Character prototype) { + this.name = name; + this.type = prototype.type; + this.prototype = prototype; + } - @Override - public boolean has(Stats stat) { - return get(stat) != null; - } + public String name() { + return name; + } - @Override - public void set(Stats stat, Integer val) { - properties.put(stat, val); - } + public Type type() { + return type; + } - @Override - public void remove(Stats stat) { - properties.put(stat, null); - } + @Override + public Integer get(Stats stat) { + boolean containsValue = properties.containsKey(stat); + if (containsValue) { + return properties.get(stat); + } else { + return prototype.get(stat); + } + } - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - if (name != null) { - builder - .append("Player: ") - .append(name) - .append("\n"); - } + @Override + public boolean has(Stats stat) { + return get(stat) != null; + } - if (type != null) { - builder - .append("Character type: ") - .append(type.name()) - .append("\n"); - } + @Override + public void set(Stats stat, Integer val) { + properties.put(stat, val); + } - builder.append("Stats:\n"); - for (Stats stat : Stats.values()) { - Integer value = this.get(stat); - if (value == null) { - continue; - } - builder - .append(" - ") - .append(stat.name()) - .append(":") - .append(value) - .append("\n"); - } - return builder.toString(); - } + @Override + public void remove(Stats stat) { + properties.put(stat, null); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + if (name != null) { + builder.append("Player: ").append(name).append('\n'); + } + + if (type != null) { + builder.append("Character type: ").append(type.name()).append('\n'); + } + + builder.append("Stats:\n"); + for (Stats stat : Stats.values()) { + Integer value = this.get(stat); + if (value == null) { + continue; + } + builder.append(" - ").append(stat.name()).append(':').append(value).append('\n'); + } + return builder.toString(); + } } diff --git a/property/src/main/java/com/iluwatar/property/Prototype.java b/property/src/main/java/com/iluwatar/property/Prototype.java index 12b4ce734..e21e98384 100644 --- a/property/src/main/java/com/iluwatar/property/Prototype.java +++ b/property/src/main/java/com/iluwatar/property/Prototype.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.property; /** @@ -5,8 +27,11 @@ package com.iluwatar.property; */ public interface Prototype { - public Integer get(Stats stat); - public boolean has(Stats stat); - public void set(Stats stat, Integer val); - public void remove(Stats stat); + Integer get(Stats stat); + + boolean has(Stats stat); + + void set(Stats stat, Integer val); + + void remove(Stats stat); } diff --git a/property/src/main/java/com/iluwatar/property/Stats.java b/property/src/main/java/com/iluwatar/property/Stats.java index b791a9dec..0c29c36f7 100644 --- a/property/src/main/java/com/iluwatar/property/Stats.java +++ b/property/src/main/java/com/iluwatar/property/Stats.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.property; /** @@ -5,5 +27,5 @@ package com.iluwatar.property; */ public enum Stats { - AGILITY, STRENGTH, ATTACK_POWER, ARMOR, INTELLECT, SPIRIT, ENERGY, RAGE + AGILITY, STRENGTH, ATTACK_POWER, ARMOR, INTELLECT, SPIRIT, ENERGY, RAGE } diff --git a/property/src/test/java/com/iluwatar/property/AppTest.java b/property/src/test/java/com/iluwatar/property/AppTest.java index dc05049f8..131742822 100644 --- a/property/src/test/java/com/iluwatar/property/AppTest.java +++ b/property/src/test/java/com/iluwatar/property/AppTest.java @@ -1,14 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.property; import org.junit.Test; -import com.iluwatar.property.App; - +/** + * + * Application test + * + */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/property/src/test/java/com/iluwatar/property/CharacterTest.java b/property/src/test/java/com/iluwatar/property/CharacterTest.java new file mode 100644 index 000000000..859ea5f86 --- /dev/null +++ b/property/src/test/java/com/iluwatar/property/CharacterTest.java @@ -0,0 +1,125 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.property; + +import org.junit.Test; + +import static com.iluwatar.property.Character.Type; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +/** + * Date: 12/28/15 - 7:46 PM + * + * @author Jeroen Meulemeester + */ +public class CharacterTest { + + @Test + public void testPrototypeStats() throws Exception { + final Character prototype = new Character(); + + for (final Stats stat : Stats.values()) { + assertFalse(prototype.has(stat)); + assertNull(prototype.get(stat)); + + final Integer expectedValue = stat.ordinal(); + prototype.set(stat, expectedValue); + assertTrue(prototype.has(stat)); + assertEquals(expectedValue, prototype.get(stat)); + + prototype.remove(stat); + assertFalse(prototype.has(stat)); + assertNull(prototype.get(stat)); + } + + } + + @Test + public void testCharacterStats() throws Exception { + final Character prototype = new Character(); + for (final Stats stat : Stats.values()) { + prototype.set(stat, stat.ordinal()); + } + + final Character mage = new Character(Type.MAGE, prototype); + for (final Stats stat : Stats.values()) { + final Integer expectedValue = stat.ordinal(); + assertTrue(mage.has(stat)); + assertEquals(expectedValue, mage.get(stat)); + } + } + + @Test + public void testToString() throws Exception { + final Character prototype = new Character(); + prototype.set(Stats.ARMOR, 1); + prototype.set(Stats.AGILITY, 2); + prototype.set(Stats.INTELLECT, 3); + assertEquals("Stats:\n - AGILITY:2\n - ARMOR:1\n - INTELLECT:3\n", prototype.toString()); + + final Character stupid = new Character(Type.ROGUE, prototype); + stupid.remove(Stats.INTELLECT); + assertEquals("Character type: ROGUE\nStats:\n - AGILITY:2\n - ARMOR:1\n", stupid.toString()); + + final Character weak = new Character("weak", prototype); + weak.remove(Stats.ARMOR); + assertEquals("Player: weak\nStats:\n - AGILITY:2\n - INTELLECT:3\n", weak.toString()); + + } + + @Test + public void testName() throws Exception { + final Character prototype = new Character(); + prototype.set(Stats.ARMOR, 1); + prototype.set(Stats.INTELLECT, 2); + assertNull(prototype.name()); + + final Character stupid = new Character(Type.ROGUE, prototype); + stupid.remove(Stats.INTELLECT); + assertNull(stupid.name()); + + final Character weak = new Character("weak", prototype); + weak.remove(Stats.ARMOR); + assertEquals("weak", weak.name()); + } + + @Test + public void testType() throws Exception { + final Character prototype = new Character(); + prototype.set(Stats.ARMOR, 1); + prototype.set(Stats.INTELLECT, 2); + assertNull(prototype.type()); + + final Character stupid = new Character(Type.ROGUE, prototype); + stupid.remove(Stats.INTELLECT); + assertEquals(Type.ROGUE, stupid.type()); + + final Character weak = new Character("weak", prototype); + weak.remove(Stats.ARMOR); + assertNull(weak.type()); + } + +} \ No newline at end of file diff --git a/prototype/README.md b/prototype/README.md new file mode 100644 index 000000000..632daca93 --- /dev/null +++ b/prototype/README.md @@ -0,0 +1,32 @@ +--- +layout: pattern +title: Prototype +folder: prototype +permalink: /patterns/prototype/ +categories: Creational +tags: + - Java + - Gang Of Four + - Difficulty-Beginner +--- + +## Intent +Specify the kinds of objects to create using a prototypical +instance, and create new objects by copying this prototype. + +![alt text](./etc/prototype_1.png "Prototype") + +## Applicability +Use the Prototype pattern when a system should be independent of how its products are created, composed and represented; and + +* when the classes to instantiate are specified at run-time, for example, by dynamic loading; or +* to avoid building a class hierarchy of factories that parallels the class hierarchy of products; or +* when instances of a class can have one of only a few different combinations of state. It may be more convenient to install a corresponding number of prototypes and clone them rather than instantiating the class manually, each time with the appropriate state + +## Real world examples + +* [java.lang.Object#clone()](http://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#clone%28%29) + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/prototype/pom.xml b/prototype/pom.xml index 31e07ca52..e8bb4303a 100644 --- a/prototype/pom.xml +++ b/prototype/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT prototype @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/prototype/src/main/java/com/iluwatar/prototype/App.java b/prototype/src/main/java/com/iluwatar/prototype/App.java index b10de963b..cb04ec5f2 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/App.java +++ b/prototype/src/main/java/com/iluwatar/prototype/App.java @@ -1,36 +1,66 @@ -package com.iluwatar.prototype; - -/** - * - * In Prototype we have a factory class (HeroFactoryImpl) producing objects by - * cloning existing ones. In this example the factory's prototype objects are - * given as constructor parameters. - * - */ -public class App { - - public static void main(String[] args) { - HeroFactory factory; - Mage mage; - Warlord warlord; - Beast beast; - - factory = new HeroFactoryImpl(new ElfMage(), new ElfWarlord(), - new ElfBeast()); - mage = factory.createMage(); - warlord = factory.createWarlord(); - beast = factory.createBeast(); - System.out.println(mage); - System.out.println(warlord); - System.out.println(beast); - - factory = new HeroFactoryImpl(new OrcMage(), new OrcWarlord(), - new OrcBeast()); - mage = factory.createMage(); - warlord = factory.createWarlord(); - beast = factory.createBeast(); - System.out.println(mage); - System.out.println(warlord); - System.out.println(beast); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.prototype; + +/** + * + * The Prototype pattern is a creational design pattern in software development. It is used when the + * type of objects to create is determined by a prototypical instance, which is cloned to produce + * new objects. This pattern is used to: - avoid subclasses of an object creator in the client + * application, like the abstract factory pattern does. - avoid the inherent cost of creating a new + * object in the standard way (e.g., using the 'new' keyword) + *

+ * In this example we have a factory class ({@link HeroFactoryImpl}) producing objects by cloning + * the existing ones. The factory's prototype objects are given as constructor parameters. + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + HeroFactory factory; + Mage mage; + Warlord warlord; + Beast beast; + + factory = new HeroFactoryImpl(new ElfMage(), new ElfWarlord(), new ElfBeast()); + mage = factory.createMage(); + warlord = factory.createWarlord(); + beast = factory.createBeast(); + System.out.println(mage); + System.out.println(warlord); + System.out.println(beast); + + factory = new HeroFactoryImpl(new OrcMage(), new OrcWarlord(), new OrcBeast()); + mage = factory.createMage(); + warlord = factory.createWarlord(); + beast = factory.createBeast(); + System.out.println(mage); + System.out.println(warlord); + System.out.println(beast); + } +} diff --git a/prototype/src/main/java/com/iluwatar/prototype/Beast.java b/prototype/src/main/java/com/iluwatar/prototype/Beast.java index 96cbfb2c8..255f44de5 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/Beast.java +++ b/prototype/src/main/java/com/iluwatar/prototype/Beast.java @@ -1,8 +1,35 @@ -package com.iluwatar.prototype; - -public abstract class Beast extends Prototype { - - @Override - public abstract Beast clone() throws CloneNotSupportedException; - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.prototype; + +/** + * + * Beast + * + */ +public abstract class Beast extends Prototype { + + @Override + public abstract Beast clone() throws CloneNotSupportedException; + +} diff --git a/prototype/src/main/java/com/iluwatar/prototype/ElfBeast.java b/prototype/src/main/java/com/iluwatar/prototype/ElfBeast.java index 17ac87511..1fd7d00fa 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/ElfBeast.java +++ b/prototype/src/main/java/com/iluwatar/prototype/ElfBeast.java @@ -1,21 +1,44 @@ -package com.iluwatar.prototype; - -public class ElfBeast extends Beast { - - public ElfBeast() { - } - - public ElfBeast(ElfBeast beast) { - } - - @Override - public Beast clone() throws CloneNotSupportedException { - return new ElfBeast(this); - } - - @Override - public String toString() { - return "Elven eagle"; - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.prototype; + +/** + * + * ElfBeast + * + */ +public class ElfBeast extends Beast { + + public ElfBeast() {} + + @Override + public Beast clone() throws CloneNotSupportedException { + return new ElfBeast(); + } + + @Override + public String toString() { + return "Elven eagle"; + } + +} diff --git a/prototype/src/main/java/com/iluwatar/prototype/ElfMage.java b/prototype/src/main/java/com/iluwatar/prototype/ElfMage.java index c41ce2dd6..823150ec4 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/ElfMage.java +++ b/prototype/src/main/java/com/iluwatar/prototype/ElfMage.java @@ -1,21 +1,44 @@ -package com.iluwatar.prototype; - -public class ElfMage extends Mage { - - public ElfMage() { - } - - public ElfMage(ElfMage mage) { - } - - @Override - public Mage clone() throws CloneNotSupportedException { - return new ElfMage(this); - } - - @Override - public String toString() { - return "Elven mage"; - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.prototype; + +/** + * + * ElfMage + * + */ +public class ElfMage extends Mage { + + public ElfMage() {} + + @Override + public Mage clone() throws CloneNotSupportedException { + return new ElfMage(); + } + + @Override + public String toString() { + return "Elven mage"; + } + +} diff --git a/prototype/src/main/java/com/iluwatar/prototype/ElfWarlord.java b/prototype/src/main/java/com/iluwatar/prototype/ElfWarlord.java index 465c1d046..03ff81ecf 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/ElfWarlord.java +++ b/prototype/src/main/java/com/iluwatar/prototype/ElfWarlord.java @@ -1,21 +1,44 @@ -package com.iluwatar.prototype; - -public class ElfWarlord extends Warlord { - - public ElfWarlord() { - } - - public ElfWarlord(ElfWarlord warlord) { - } - - @Override - public Warlord clone() throws CloneNotSupportedException { - return new ElfWarlord(this); - } - - @Override - public String toString() { - return "Elven warlord"; - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.prototype; + +/** + * + * ElfWarlord + * + */ +public class ElfWarlord extends Warlord { + + public ElfWarlord() {} + + @Override + public Warlord clone() throws CloneNotSupportedException { + return new ElfWarlord(); + } + + @Override + public String toString() { + return "Elven warlord"; + } + +} diff --git a/prototype/src/main/java/com/iluwatar/prototype/HeroFactory.java b/prototype/src/main/java/com/iluwatar/prototype/HeroFactory.java index 468eefcb5..c152ae3e8 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/HeroFactory.java +++ b/prototype/src/main/java/com/iluwatar/prototype/HeroFactory.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.prototype; /** @@ -7,10 +29,10 @@ package com.iluwatar.prototype; */ public interface HeroFactory { - Mage createMage(); + Mage createMage(); - Warlord createWarlord(); + Warlord createWarlord(); - Beast createBeast(); + Beast createBeast(); } diff --git a/prototype/src/main/java/com/iluwatar/prototype/HeroFactoryImpl.java b/prototype/src/main/java/com/iluwatar/prototype/HeroFactoryImpl.java index dc440f41e..ba173d9bc 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/HeroFactoryImpl.java +++ b/prototype/src/main/java/com/iluwatar/prototype/HeroFactoryImpl.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.prototype; /** @@ -7,38 +29,50 @@ package com.iluwatar.prototype; */ public class HeroFactoryImpl implements HeroFactory { - private Mage mage; - private Warlord warlord; - private Beast beast; + private Mage mage; + private Warlord warlord; + private Beast beast; - public HeroFactoryImpl(Mage mage, Warlord warlord, Beast beast) { - this.mage = mage; - this.warlord = warlord; - this.beast = beast; - } + /** + * Constructor + */ + public HeroFactoryImpl(Mage mage, Warlord warlord, Beast beast) { + this.mage = mage; + this.warlord = warlord; + this.beast = beast; + } - public Mage createMage() { - try { - return mage.clone(); - } catch (CloneNotSupportedException e) { - return null; - } - } + /** + * Create mage + */ + public Mage createMage() { + try { + return mage.clone(); + } catch (CloneNotSupportedException e) { + return null; + } + } - public Warlord createWarlord() { - try { - return warlord.clone(); - } catch (CloneNotSupportedException e) { - return null; - } - } + /** + * Create warlord + */ + public Warlord createWarlord() { + try { + return warlord.clone(); + } catch (CloneNotSupportedException e) { + return null; + } + } - public Beast createBeast() { - try { - return beast.clone(); - } catch (CloneNotSupportedException e) { - return null; - } - } + /** + * Create beast + */ + public Beast createBeast() { + try { + return beast.clone(); + } catch (CloneNotSupportedException e) { + return null; + } + } } diff --git a/prototype/src/main/java/com/iluwatar/prototype/Mage.java b/prototype/src/main/java/com/iluwatar/prototype/Mage.java index 99cee8893..3151fc915 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/Mage.java +++ b/prototype/src/main/java/com/iluwatar/prototype/Mage.java @@ -1,8 +1,35 @@ -package com.iluwatar.prototype; - -public abstract class Mage extends Prototype { - - @Override - public abstract Mage clone() throws CloneNotSupportedException; - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.prototype; + +/** + * + * Mage + * + */ +public abstract class Mage extends Prototype { + + @Override + public abstract Mage clone() throws CloneNotSupportedException; + +} diff --git a/prototype/src/main/java/com/iluwatar/prototype/OrcBeast.java b/prototype/src/main/java/com/iluwatar/prototype/OrcBeast.java index bae7d360e..8be1e0b93 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/OrcBeast.java +++ b/prototype/src/main/java/com/iluwatar/prototype/OrcBeast.java @@ -1,21 +1,44 @@ -package com.iluwatar.prototype; - -public class OrcBeast extends Beast { - - public OrcBeast() { - } - - public OrcBeast(OrcBeast beast) { - } - - @Override - public Beast clone() throws CloneNotSupportedException { - return new OrcBeast(this); - } - - @Override - public String toString() { - return "Orcish wolf"; - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.prototype; + +/** + * + * OrcBeast + * + */ +public class OrcBeast extends Beast { + + public OrcBeast() {} + + @Override + public Beast clone() throws CloneNotSupportedException { + return new OrcBeast(); + } + + @Override + public String toString() { + return "Orcish wolf"; + } + +} diff --git a/prototype/src/main/java/com/iluwatar/prototype/OrcMage.java b/prototype/src/main/java/com/iluwatar/prototype/OrcMage.java index a329ca61b..3aedc1ca1 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/OrcMage.java +++ b/prototype/src/main/java/com/iluwatar/prototype/OrcMage.java @@ -1,21 +1,44 @@ -package com.iluwatar.prototype; - -public class OrcMage extends Mage { - - public OrcMage() { - } - - public OrcMage(OrcMage mage) { - } - - @Override - public Mage clone() throws CloneNotSupportedException { - return new OrcMage(this); - } - - @Override - public String toString() { - return "Orcish mage"; - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.prototype; + +/** + * + * OrcMage + * + */ +public class OrcMage extends Mage { + + public OrcMage() {} + + @Override + public Mage clone() throws CloneNotSupportedException { + return new OrcMage(); + } + + @Override + public String toString() { + return "Orcish mage"; + } + +} diff --git a/prototype/src/main/java/com/iluwatar/prototype/OrcWarlord.java b/prototype/src/main/java/com/iluwatar/prototype/OrcWarlord.java index 2832c3835..1cd4e72b7 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/OrcWarlord.java +++ b/prototype/src/main/java/com/iluwatar/prototype/OrcWarlord.java @@ -1,21 +1,44 @@ -package com.iluwatar.prototype; - -public class OrcWarlord extends Warlord { - - public OrcWarlord() { - } - - public OrcWarlord(OrcWarlord warlord) { - } - - @Override - public Warlord clone() throws CloneNotSupportedException { - return new OrcWarlord(this); - } - - @Override - public String toString() { - return "Orcish warlord"; - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.prototype; + +/** + * + * OrcWarlord + * + */ +public class OrcWarlord extends Warlord { + + public OrcWarlord() {} + + @Override + public Warlord clone() throws CloneNotSupportedException { + return new OrcWarlord(); + } + + @Override + public String toString() { + return "Orcish warlord"; + } + +} diff --git a/prototype/src/main/java/com/iluwatar/prototype/Prototype.java b/prototype/src/main/java/com/iluwatar/prototype/Prototype.java index 2329f1e6d..bf7980c4d 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/Prototype.java +++ b/prototype/src/main/java/com/iluwatar/prototype/Prototype.java @@ -1,8 +1,35 @@ -package com.iluwatar.prototype; - -public abstract class Prototype implements Cloneable { - - @Override - public abstract Object clone() throws CloneNotSupportedException; - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.prototype; + +/** + * + * Prototype + * + */ +public abstract class Prototype implements Cloneable { + + @Override + public abstract Object clone() throws CloneNotSupportedException; + +} diff --git a/prototype/src/main/java/com/iluwatar/prototype/Warlord.java b/prototype/src/main/java/com/iluwatar/prototype/Warlord.java index 930320d97..72b2cfa80 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/Warlord.java +++ b/prototype/src/main/java/com/iluwatar/prototype/Warlord.java @@ -1,8 +1,35 @@ -package com.iluwatar.prototype; - -public abstract class Warlord extends Prototype { - - @Override - public abstract Warlord clone() throws CloneNotSupportedException; - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.prototype; + +/** + * + * Warlord + * + */ +public abstract class Warlord extends Prototype { + + @Override + public abstract Warlord clone() throws CloneNotSupportedException; + +} diff --git a/prototype/src/test/java/com/iluwatar/prototype/AppTest.java b/prototype/src/test/java/com/iluwatar/prototype/AppTest.java index ecb576038..4b06f957c 100644 --- a/prototype/src/test/java/com/iluwatar/prototype/AppTest.java +++ b/prototype/src/test/java/com/iluwatar/prototype/AppTest.java @@ -1,14 +1,39 @@ -package com.iluwatar.prototype; - -import org.junit.Test; - -import com.iluwatar.prototype.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.prototype; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/prototype/src/test/java/com/iluwatar/prototype/HeroFactoryImplTest.java b/prototype/src/test/java/com/iluwatar/prototype/HeroFactoryImplTest.java new file mode 100644 index 000000000..75a234a57 --- /dev/null +++ b/prototype/src/test/java/com/iluwatar/prototype/HeroFactoryImplTest.java @@ -0,0 +1,61 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.prototype; + +import org.junit.Test; + +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +/** + * Date: 12/28/15 - 8:34 PM + * + * @author Jeroen Meulemeester + */ +public class HeroFactoryImplTest { + + @Test + public void testFactory() throws Exception { + final Mage mage = mock(Mage.class); + final Warlord warlord = mock(Warlord.class); + final Beast beast = mock(Beast.class); + + when(mage.clone()).thenThrow(CloneNotSupportedException.class); + when(warlord.clone()).thenThrow(CloneNotSupportedException.class); + when(beast.clone()).thenThrow(CloneNotSupportedException.class); + + final HeroFactoryImpl factory = new HeroFactoryImpl(mage, warlord, beast); + assertNull(factory.createMage()); + assertNull(factory.createWarlord()); + assertNull(factory.createBeast()); + + verify(mage).clone(); + verify(warlord).clone(); + verify(beast).clone(); + verifyNoMoreInteractions(mage, warlord, beast); + } + +} \ No newline at end of file diff --git a/prototype/src/test/java/com/iluwatar/prototype/PrototypeTest.java b/prototype/src/test/java/com/iluwatar/prototype/PrototypeTest.java new file mode 100644 index 000000000..071b4dd0d --- /dev/null +++ b/prototype/src/test/java/com/iluwatar/prototype/PrototypeTest.java @@ -0,0 +1,88 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.prototype; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; +import java.util.Collection; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; + +/** + * Date: 12/28/15 - 8:45 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class PrototypeTest

{ + + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList( + new Object[]{new OrcBeast(), "Orcish wolf"}, + new Object[]{new OrcMage(), "Orcish mage"}, + new Object[]{new OrcWarlord(), "Orcish warlord"}, + new Object[]{new ElfBeast(), "Elven eagle"}, + new Object[]{new ElfMage(), "Elven mage"}, + new Object[]{new ElfWarlord(), "Elven warlord"} + ); + } + + /** + * The tested prototype instance + */ + private final Prototype testedPrototype; + + /** + * The expected {@link Prototype#toString()} value + */ + private final String expectedToString; + + /** + * Create a new test instance, using the given test object and expected value + * + * @param testedPrototype The tested prototype instance + * @param expectedToString The expected {@link Prototype#toString()} value + */ + public PrototypeTest(final Prototype testedPrototype, final String expectedToString) { + this.expectedToString = expectedToString; + this.testedPrototype = testedPrototype; + } + + @Test + public void testPrototype() throws Exception { + assertEquals(this.expectedToString, this.testedPrototype.toString()); + + final Object clone = this.testedPrototype.clone(); + assertNotNull(clone); + assertNotSame(clone, this.testedPrototype); + assertSame(this.testedPrototype.getClass(), clone.getClass()); + } + +} diff --git a/proxy/README.md b/proxy/README.md new file mode 100644 index 000000000..a3e03708e --- /dev/null +++ b/proxy/README.md @@ -0,0 +1,46 @@ +--- +layout: pattern +title: Proxy +folder: proxy +permalink: /patterns/proxy/ +categories: Structural +tags: + - Java + - Gang Of Four + - Difficulty-Beginner +--- + +## Also known as +Surrogate + +## Intent +Provide a surrogate or placeholder for another object to control +access to it. + +![alt text](./etc/proxy_1.png "Proxy") + +## Applicability +Proxy is applicable whenever there is a need for a more +versatile or sophisticated reference to an object than a simple pointer. Here +are several common situations in which the Proxy pattern is applicable + +* a remote proxy provides a local representative for an object in a different address space. +* a virtual proxy creates expensive objects on demand. +* a protection proxy controls access to the original object. Protection proxies are useful when objects should have different access rights. + +## Typical Use Case + +* control access to another object +* lazy initialization +* implement logging +* facilitate network connection +* to count references to an object + +## Real world examples + +* [java.lang.reflect.Proxy](http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Proxy.html) +* [Apache Commons Proxy](https://commons.apache.org/proper/commons-proxy/) + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/proxy/pom.xml b/proxy/pom.xml index a33f4f612..f16736a2c 100644 --- a/proxy/pom.xml +++ b/proxy/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT proxy @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/proxy/src/main/java/com/iluwatar/proxy/App.java b/proxy/src/main/java/com/iluwatar/proxy/App.java index 86c96466b..9c27cfb01 100644 --- a/proxy/src/main/java/com/iluwatar/proxy/App.java +++ b/proxy/src/main/java/com/iluwatar/proxy/App.java @@ -1,20 +1,56 @@ -package com.iluwatar.proxy; - -/** - * - * Proxy (WizardTowerProxy) controls access to the actual object (WizardTower). - * - */ -public class App { - - public static void main(String[] args) { - - WizardTowerProxy tower = new WizardTowerProxy(); - tower.enter(new Wizard("Red wizard")); - tower.enter(new Wizard("White wizard")); - tower.enter(new Wizard("Black wizard")); - tower.enter(new Wizard("Green wizard")); - tower.enter(new Wizard("Brown wizard")); - - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.proxy; + +/** + * + * A proxy, in its most general form, is a class functioning as an interface to something else. The + * proxy could interface to anything: a network connection, a large object in memory, a file, or + * some other resource that is expensive or impossible to duplicate. In short, a proxy is a wrapper + * or agent object that is being called by the client to access the real serving object behind the + * scenes. + *

+ * The Proxy design pattern allows you to provide an interface to other objects by creating a + * wrapper class as the proxy. The wrapper class, which is the proxy, can add additional + * functionality to the object of interest without changing the object's code. + *

+ * In this example the proxy ({@link WizardTowerProxy}) controls access to the actual object ( + * {@link WizardTower}). + * + */ +public class App { + + /** + * Program entry point + */ + public static void main(String[] args) { + + WizardTowerProxy tower = new WizardTowerProxy(); + tower.enter(new Wizard("Red wizard")); + tower.enter(new Wizard("White wizard")); + tower.enter(new Wizard("Black wizard")); + tower.enter(new Wizard("Green wizard")); + tower.enter(new Wizard("Brown wizard")); + + } +} diff --git a/proxy/src/main/java/com/iluwatar/proxy/Wizard.java b/proxy/src/main/java/com/iluwatar/proxy/Wizard.java index 2060a5803..5ea2e8279 100644 --- a/proxy/src/main/java/com/iluwatar/proxy/Wizard.java +++ b/proxy/src/main/java/com/iluwatar/proxy/Wizard.java @@ -1,16 +1,43 @@ -package com.iluwatar.proxy; - -public class Wizard { - - private String name; - - public Wizard(String name) { - this.name = name; - } - - @Override - public String toString() { - return name; - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.proxy; + +/** + * + * Wizard + * + */ +public class Wizard { + + private String name; + + public Wizard(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + +} diff --git a/proxy/src/main/java/com/iluwatar/proxy/WizardTower.java b/proxy/src/main/java/com/iluwatar/proxy/WizardTower.java index d4aa27c81..573d38374 100644 --- a/proxy/src/main/java/com/iluwatar/proxy/WizardTower.java +++ b/proxy/src/main/java/com/iluwatar/proxy/WizardTower.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.proxy; /** @@ -7,8 +29,8 @@ package com.iluwatar.proxy; */ public class WizardTower { - public void enter(Wizard wizard) { - System.out.println(wizard + " enters the tower."); - } + public void enter(Wizard wizard) { + System.out.println(wizard + " enters the tower."); + } } diff --git a/proxy/src/main/java/com/iluwatar/proxy/WizardTowerProxy.java b/proxy/src/main/java/com/iluwatar/proxy/WizardTowerProxy.java index 3563aef1d..985184afe 100644 --- a/proxy/src/main/java/com/iluwatar/proxy/WizardTowerProxy.java +++ b/proxy/src/main/java/com/iluwatar/proxy/WizardTowerProxy.java @@ -1,23 +1,45 @@ -package com.iluwatar.proxy; - -/** - * - * The proxy controlling access to WizardTower. - * - */ -public class WizardTowerProxy extends WizardTower { - - private static final int NUM_WIZARDS_ALLOWED = 3; - - private int numWizards; - - @Override - public void enter(Wizard wizard) { - if (numWizards < NUM_WIZARDS_ALLOWED) { - super.enter(wizard); - numWizards++; - } else { - System.out.println(wizard + " is not allowed to enter!"); - } - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.proxy; + +/** + * + * The proxy controlling access to the {@link WizardTower}. + * + */ +public class WizardTowerProxy extends WizardTower { + + private static final int NUM_WIZARDS_ALLOWED = 3; + + private int numWizards; + + @Override + public void enter(Wizard wizard) { + if (numWizards < NUM_WIZARDS_ALLOWED) { + super.enter(wizard); + numWizards++; + } else { + System.out.println(wizard + " is not allowed to enter!"); + } + } +} diff --git a/proxy/src/test/java/com/iluwatar/proxy/AppTest.java b/proxy/src/test/java/com/iluwatar/proxy/AppTest.java index 0cd9641df..e5c065bd5 100644 --- a/proxy/src/test/java/com/iluwatar/proxy/AppTest.java +++ b/proxy/src/test/java/com/iluwatar/proxy/AppTest.java @@ -1,14 +1,39 @@ -package com.iluwatar.proxy; - -import org.junit.Test; - -import com.iluwatar.proxy.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.proxy; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/proxy/src/test/java/com/iluwatar/proxy/StdOutTest.java b/proxy/src/test/java/com/iluwatar/proxy/StdOutTest.java new file mode 100644 index 000000000..48831444a --- /dev/null +++ b/proxy/src/test/java/com/iluwatar/proxy/StdOutTest.java @@ -0,0 +1,75 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.proxy; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since some actions don't have any + * influence on accessible objects, except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/proxy/src/test/java/com/iluwatar/proxy/WizardTest.java b/proxy/src/test/java/com/iluwatar/proxy/WizardTest.java new file mode 100644 index 000000000..56ad74c8c --- /dev/null +++ b/proxy/src/test/java/com/iluwatar/proxy/WizardTest.java @@ -0,0 +1,44 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.proxy; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Date: 12/28/15 - 9:02 PM + * + * @author Jeroen Meulemeester + */ +public class WizardTest { + + @Test + public void testToString() throws Exception { + final String[] wizardNames = {"Gandalf", "Dumbledore", "Oz", "Merlin"}; + for (final String name : wizardNames) { + assertEquals(name, new Wizard(name).toString()); + } + } + +} \ No newline at end of file diff --git a/proxy/src/test/java/com/iluwatar/proxy/WizardTowerProxyTest.java b/proxy/src/test/java/com/iluwatar/proxy/WizardTowerProxyTest.java new file mode 100644 index 000000000..b87b7a0bc --- /dev/null +++ b/proxy/src/test/java/com/iluwatar/proxy/WizardTowerProxyTest.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.proxy; + +import org.junit.Test; +import org.mockito.InOrder; + +import static org.mockito.Mockito.inOrder; + +/** + * Date: 12/28/15 - 9:18 PM + * + * @author Jeroen Meulemeester + */ +public class WizardTowerProxyTest extends StdOutTest { + + @Test + public void testEnter() throws Exception { + final Wizard[] wizards = new Wizard[]{ + new Wizard("Gandalf"), + new Wizard("Dumbledore"), + new Wizard("Oz"), + new Wizard("Merlin") + }; + + final WizardTowerProxy tower = new WizardTowerProxy(); + for (final Wizard wizard : wizards) { + tower.enter(wizard); + } + + final InOrder inOrder = inOrder(getStdOutMock()); + inOrder.verify(getStdOutMock()).println("Gandalf enters the tower."); + inOrder.verify(getStdOutMock()).println("Dumbledore enters the tower."); + inOrder.verify(getStdOutMock()).println("Oz enters the tower."); + inOrder.verify(getStdOutMock()).println("Merlin is not allowed to enter!"); + inOrder.verifyNoMoreInteractions(); + + } + +} \ No newline at end of file diff --git a/proxy/src/test/java/com/iluwatar/proxy/WizardTowerTest.java b/proxy/src/test/java/com/iluwatar/proxy/WizardTowerTest.java new file mode 100644 index 000000000..9996434f5 --- /dev/null +++ b/proxy/src/test/java/com/iluwatar/proxy/WizardTowerTest.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.proxy; + +import org.junit.Test; +import org.mockito.InOrder; + +import static org.mockito.Mockito.inOrder; + +/** + * Date: 12/28/15 - 9:18 PM + * + * @author Jeroen Meulemeester + */ +public class WizardTowerTest extends StdOutTest { + + @Test + public void testEnter() throws Exception { + final Wizard[] wizards = new Wizard[]{ + new Wizard("Gandalf"), + new Wizard("Dumbledore"), + new Wizard("Oz"), + new Wizard("Merlin") + }; + + final WizardTower tower = new WizardTower(); + for (final Wizard wizard : wizards) { + tower.enter(wizard); + } + + final InOrder inOrder = inOrder(getStdOutMock()); + inOrder.verify(getStdOutMock()).println("Gandalf enters the tower."); + inOrder.verify(getStdOutMock()).println("Dumbledore enters the tower."); + inOrder.verify(getStdOutMock()).println("Oz enters the tower."); + inOrder.verify(getStdOutMock()).println("Merlin enters the tower."); + inOrder.verifyNoMoreInteractions(); + + } + +} \ No newline at end of file diff --git a/publish-subscribe/.gitignore b/publish-subscribe/.gitignore new file mode 100644 index 000000000..b83d22266 --- /dev/null +++ b/publish-subscribe/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/publish-subscribe/README.md b/publish-subscribe/README.md new file mode 100644 index 000000000..6a5b2dfa8 --- /dev/null +++ b/publish-subscribe/README.md @@ -0,0 +1,25 @@ +--- +layout: pattern +title: Publish Subscribe +folder: publish-subscribe +permalink: /patterns/publish-subscribe/ +categories: Integration +tags: + - Java + - EIP + - Apache Camel™ +--- + +## Intent +Broadcast messages from sender to all the interested receivers. + +![alt text](./etc/publish-subscribe.png "Publish Subscribe Channel") + +## Applicability +Use the Publish Subscribe Channel pattern when + +* two or more applications need to communicate using a messaging system for broadcasts. + +## Credits + +* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) diff --git a/publish-subscribe/etc/publish-subscribe.png b/publish-subscribe/etc/publish-subscribe.png new file mode 100644 index 000000000..99867da66 Binary files /dev/null and b/publish-subscribe/etc/publish-subscribe.png differ diff --git a/publish-subscribe/etc/publish-subscribe.ucls b/publish-subscribe/etc/publish-subscribe.ucls new file mode 100644 index 000000000..1b121506e --- /dev/null +++ b/publish-subscribe/etc/publish-subscribe.ucls @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/publish-subscribe/pom.xml b/publish-subscribe/pom.xml new file mode 100644 index 000000000..6db6b3538 --- /dev/null +++ b/publish-subscribe/pom.xml @@ -0,0 +1,48 @@ + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + publish-subscribe + + + org.apache.camel + camel-core + + + org.apache.camel + camel-stream + + + junit + junit + + + \ No newline at end of file diff --git a/publish-subscribe/src/main/java/com/iluwatar/publish/subscribe/App.java b/publish-subscribe/src/main/java/com/iluwatar/publish/subscribe/App.java new file mode 100644 index 000000000..c4e423b04 --- /dev/null +++ b/publish-subscribe/src/main/java/com/iluwatar/publish/subscribe/App.java @@ -0,0 +1,68 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.publish.subscribe; + +import org.apache.camel.CamelContext; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.impl.DefaultCamelContext; + +/** + * + * There are well-established patterns for implementing broadcasting. The Observer pattern describes + * the need to decouple observers from their subject (that is, the originator of the event) so that + * the subject can easily provide event notification to all interested observers no matter how many + * observers there are (even none). The Publish-Subscribe pattern expands upon Observer by adding + * the notion of an event channel for communicating event notifications. + *

+ * A Publish-Subscribe Channel works like this: It has one input channel that splits into multiple + * output channels, one for each subscriber. When an event is published into the channel, the + * Publish-Subscribe Channel delivers a copy of the message to each of the output channels. Each + * output end of the channel has only one subscriber, which is allowed to consume a message only + * once. In this way, each subscriber gets the message only once, and consumed copies disappear from + * their channels. + *

+ * In this example we use Apache Camel to establish a Publish-Subscribe Channel from "direct-origin" + * to "mock:foo", "mock:bar" and "stream:out". + * + */ +public class App { + + /** + * Program entry point + */ + public static void main(String[] args) throws Exception { + CamelContext context = new DefaultCamelContext(); + context.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:origin").multicast().to("mock:foo", "mock:bar", "stream:out"); + } + }); + ProducerTemplate template = context.createProducerTemplate(); + context.start(); + context.getRoutes().stream().forEach(r -> System.out.println(r)); + template.sendBody("direct:origin", "Hello from origin"); + context.stop(); + } +} diff --git a/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/AppTest.java b/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/AppTest.java new file mode 100644 index 000000000..9f387be33 --- /dev/null +++ b/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/AppTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.publish.subscribe; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() throws Exception { + String[] args = {}; + App.main(args); + } +} diff --git a/reactor/README.md b/reactor/README.md new file mode 100644 index 000000000..b9ba98948 --- /dev/null +++ b/reactor/README.md @@ -0,0 +1,34 @@ +--- +layout: pattern +title: Reactor +folder: reactor +permalink: /patterns/reactor/ +categories: Concurrency +tags: + - Java + - Difficulty-Expert + - I/O +--- + +## Intent +The Reactor design pattern handles service requests that are delivered concurrently to an application by one or more clients. The application can register specific handlers for processing which are called by reactor on specific events. Dispatching of event handlers is performed by an initiation dispatcher, which manages the registered event handlers. Demultiplexing of service requests is performed by a synchronous event demultiplexer. + +![Reactor](./etc/reactor.png "Reactor") + +## Applicability +Use Reactor pattern when + +* a server application needs to handle concurrent service requests from multiple clients. +* a server application needs to be available for receiving requests from new clients even when handling older client requests. +* a server must maximize throughput, minimize latency and use CPU efficiently without blocking. + +## Real world examples + +* [Spring Reactor](http://projectreactor.io/) + +## Credits + +* [Douglas C. Schmidt - Reactor](https://www.dre.vanderbilt.edu/~schmidt/PDF/Reactor.pdf) +* [Pattern Oriented Software Architecture Vol I-V](http://www.amazon.com/Pattern-Oriented-Software-Architecture-Volume-Patterns/dp/0471958697) +* [Doug Lea - Scalable IO in Java](http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf) +* [Netty](http://netty.io/) diff --git a/reactor/etc/reactor.png b/reactor/etc/reactor.png new file mode 100644 index 000000000..abe705682 Binary files /dev/null and b/reactor/etc/reactor.png differ diff --git a/reactor/etc/reactor.ucls b/reactor/etc/reactor.ucls new file mode 100644 index 000000000..90e28cdd7 --- /dev/null +++ b/reactor/etc/reactor.ucls @@ -0,0 +1,235 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/reactor/pom.xml b/reactor/pom.xml new file mode 100644 index 000000000..c06584c6f --- /dev/null +++ b/reactor/pom.xml @@ -0,0 +1,42 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + reactor + + + junit + junit + test + + + diff --git a/reactor/src/main/java/com/iluwatar/reactor/app/App.java b/reactor/src/main/java/com/iluwatar/reactor/app/App.java new file mode 100644 index 000000000..4228b92d3 --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/app/App.java @@ -0,0 +1,163 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.reactor.app; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import com.iluwatar.reactor.framework.AbstractNioChannel; +import com.iluwatar.reactor.framework.ChannelHandler; +import com.iluwatar.reactor.framework.Dispatcher; +import com.iluwatar.reactor.framework.NioDatagramChannel; +import com.iluwatar.reactor.framework.NioReactor; +import com.iluwatar.reactor.framework.NioServerSocketChannel; +import com.iluwatar.reactor.framework.ThreadPoolDispatcher; + +/** + * This application demonstrates Reactor pattern. The example demonstrated is a Distributed Logging + * Service where it listens on multiple TCP or UDP sockets for incoming log requests. + * + *

+ * INTENT
+ * The Reactor design pattern handles service requests that are delivered concurrently to an + * application by one or more clients. The application can register specific handlers for processing + * which are called by reactor on specific events. + * + *

+ * PROBLEM
+ * Server applications in a distributed system must handle multiple clients that send them service + * requests. Following forces need to be resolved: + *

    + *
  • Availability
  • + *
  • Efficiency
  • + *
  • Programming Simplicity
  • + *
  • Adaptability
  • + *
+ * + *

+ * PARTICIPANTS
+ *

    + *
  • Synchronous Event De-multiplexer
  • {@link NioReactor} plays the role of synchronous event + * de-multiplexer. It waits for events on multiple channels registered to it in an event loop. + * + *

    + *

  • Initiation Dispatcher
  • {@link NioReactor} plays this role as the application specific + * {@link ChannelHandler}s are registered to the reactor. + * + *

    + *

  • Handle
  • {@link AbstractNioChannel} acts as a handle that is registered to the reactor. + * When any events occur on a handle, reactor calls the appropriate handler. + * + *

    + *

  • Event Handler
  • {@link ChannelHandler} acts as an event handler, which is bound to a + * channel and is called back when any event occurs on any of its associated handles. Application + * logic resides in event handlers. + *
+ * + *

+ * The application utilizes single thread to listen for requests on all ports. It does not create a + * separate thread for each client, which provides better scalability under load (number of clients + * increase). + * + *

+ * The example uses Java NIO framework to implement the Reactor. + * + */ +public class App { + + private NioReactor reactor; + private List channels = new ArrayList<>(); + private Dispatcher dispatcher; + + /** + * Creates an instance of App which will use provided dispatcher for dispatching events on + * reactor. + * + * @param dispatcher the dispatcher that will be used to dispatch events. + */ + public App(Dispatcher dispatcher) { + this.dispatcher = dispatcher; + } + + /** + * App entry. + */ + public static void main(String[] args) throws IOException { + new App(new ThreadPoolDispatcher(2)).start(); + } + + /** + * Starts the NIO reactor. + * + * @throws IOException if any channel fails to bind. + */ + public void start() throws IOException { + /* + * The application can customize its event dispatching mechanism. + */ + reactor = new NioReactor(dispatcher); + + /* + * This represents application specific business logic that dispatcher will call on appropriate + * events. These events are read events in our example. + */ + LoggingHandler loggingHandler = new LoggingHandler(); + + /* + * Our application binds to multiple channels and uses same logging handler to handle incoming + * log requests. + */ + reactor.registerChannel(tcpChannel(6666, loggingHandler)) + .registerChannel(tcpChannel(6667, loggingHandler)) + .registerChannel(udpChannel(6668, loggingHandler)).start(); + } + + /** + * Stops the NIO reactor. This is a blocking call. + * + * @throws InterruptedException if interrupted while stopping the reactor. + * @throws IOException if any I/O error occurs + */ + public void stop() throws InterruptedException, IOException { + reactor.stop(); + dispatcher.stop(); + for (AbstractNioChannel channel : channels) { + channel.getJavaChannel().close(); + } + } + + private AbstractNioChannel tcpChannel(int port, ChannelHandler handler) throws IOException { + NioServerSocketChannel channel = new NioServerSocketChannel(port, handler); + channel.bind(); + channels.add(channel); + return channel; + } + + private AbstractNioChannel udpChannel(int port, ChannelHandler handler) throws IOException { + NioDatagramChannel channel = new NioDatagramChannel(port, handler); + channel.bind(); + channels.add(channel); + return channel; + } +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java b/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java new file mode 100644 index 000000000..29c2f83fa --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java @@ -0,0 +1,186 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.reactor.app; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * Represents the clients of Reactor pattern. Multiple clients are run concurrently and send logging + * requests to Reactor. + */ +public class AppClient { + private final ExecutorService service = Executors.newFixedThreadPool(4); + + /** + * App client entry. + * + * @throws IOException if any I/O error occurs. + */ + public static void main(String[] args) throws IOException { + AppClient appClient = new AppClient(); + appClient.start(); + } + + /** + * Starts the logging clients. + * + * @throws IOException if any I/O error occurs. + */ + public void start() throws IOException { + service.execute(new TcpLoggingClient("Client 1", 6666)); + service.execute(new TcpLoggingClient("Client 2", 6667)); + service.execute(new UdpLoggingClient("Client 3", 6668)); + service.execute(new UdpLoggingClient("Client 4", 6668)); + } + + /** + * Stops logging clients. This is a blocking call. + */ + public void stop() { + service.shutdown(); + if (!service.isTerminated()) { + service.shutdownNow(); + try { + service.awaitTermination(1000, TimeUnit.SECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + private static void artificialDelayOf(long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + /** + * A logging client that sends requests to Reactor on TCP socket. + */ + static class TcpLoggingClient implements Runnable { + + private final int serverPort; + private final String clientName; + + /** + * Creates a new TCP logging client. + * + * @param clientName the name of the client to be sent in logging requests. + * @param port the port on which client will send logging requests. + */ + public TcpLoggingClient(String clientName, int serverPort) { + this.clientName = clientName; + this.serverPort = serverPort; + } + + public void run() { + try (Socket socket = new Socket(InetAddress.getLocalHost(), serverPort)) { + OutputStream outputStream = socket.getOutputStream(); + PrintWriter writer = new PrintWriter(outputStream); + sendLogRequests(writer, socket.getInputStream()); + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + private void sendLogRequests(PrintWriter writer, InputStream inputStream) throws IOException { + for (int i = 0; i < 4; i++) { + writer.println(clientName + " - Log request: " + i); + writer.flush(); + + byte[] data = new byte[1024]; + int read = inputStream.read(data, 0, data.length); + if (read == 0) { + System.out.println("Read zero bytes"); + } else { + System.out.println(new String(data, 0, read)); + } + + artificialDelayOf(100); + } + } + + } + + /** + * A logging client that sends requests to Reactor on UDP socket. + */ + static class UdpLoggingClient implements Runnable { + private final String clientName; + private final InetSocketAddress remoteAddress; + + /** + * Creates a new UDP logging client. + * + * @param clientName the name of the client to be sent in logging requests. + * @param port the port on which client will send logging requests. + * @throws UnknownHostException if localhost is unknown + */ + public UdpLoggingClient(String clientName, int port) throws UnknownHostException { + this.clientName = clientName; + this.remoteAddress = new InetSocketAddress(InetAddress.getLocalHost(), port); + } + + @Override + public void run() { + try (DatagramSocket socket = new DatagramSocket()) { + for (int i = 0; i < 4; i++) { + + String message = clientName + " - Log request: " + i; + DatagramPacket request = + new DatagramPacket(message.getBytes(), message.getBytes().length, remoteAddress); + + socket.send(request); + + byte[] data = new byte[1024]; + DatagramPacket reply = new DatagramPacket(data, data.length); + socket.receive(reply); + if (reply.getLength() == 0) { + System.out.println("Read zero bytes"); + } else { + System.out.println(new String(reply.getData(), 0, reply.getLength())); + } + + artificialDelayOf(100); + } + } catch (IOException e1) { + e1.printStackTrace(); + } + } + } +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java b/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java new file mode 100644 index 000000000..ab2bcfb1a --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java @@ -0,0 +1,81 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.reactor.app; + +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; + +import com.iluwatar.reactor.framework.AbstractNioChannel; +import com.iluwatar.reactor.framework.ChannelHandler; +import com.iluwatar.reactor.framework.NioDatagramChannel.DatagramPacket; + +/** + * Logging server application logic. It logs the incoming requests on standard console and returns a + * canned acknowledgement back to the remote peer. + */ +public class LoggingHandler implements ChannelHandler { + + private static final byte[] ACK = "Data logged successfully".getBytes(); + + /** + * Decodes the received data and logs it on standard console. + */ + @Override + public void handleChannelRead(AbstractNioChannel channel, Object readObject, SelectionKey key) { + /* + * As this handler is attached with both TCP and UDP channels we need to check whether the data + * received is a ByteBuffer (from TCP channel) or a DatagramPacket (from UDP channel). + */ + if (readObject instanceof ByteBuffer) { + doLogging((ByteBuffer) readObject); + sendReply(channel, key); + } else if (readObject instanceof DatagramPacket) { + DatagramPacket datagram = (DatagramPacket) readObject; + doLogging(datagram.getData()); + sendReply(channel, datagram, key); + } else { + throw new IllegalStateException("Unknown data received"); + } + } + + private static void sendReply(AbstractNioChannel channel, DatagramPacket incomingPacket, SelectionKey key) { + /* + * Create a reply acknowledgement datagram packet setting the receiver to the sender of incoming + * message. + */ + DatagramPacket replyPacket = new DatagramPacket(ByteBuffer.wrap(ACK)); + replyPacket.setReceiver(incomingPacket.getSender()); + + channel.write(replyPacket, key); + } + + private static void sendReply(AbstractNioChannel channel, SelectionKey key) { + ByteBuffer buffer = ByteBuffer.wrap(ACK); + channel.write(buffer, key); + } + + private static void doLogging(ByteBuffer data) { + // assuming UTF-8 :( + System.out.println(new String(data.array(), 0, data.limit())); + } +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java b/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java new file mode 100644 index 000000000..ee830e3b1 --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java @@ -0,0 +1,175 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.reactor.framework; + +import java.io.IOException; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * This represents the Handle of Reactor pattern. These are resources managed by OS which can + * be submitted to {@link NioReactor}. + * + *

+ * This class serves has the responsibility of reading the data when a read event occurs and writing + * the data back when the channel is writable. It leaves the reading and writing of data on the + * concrete implementation. It provides a block writing mechanism wherein when any + * {@link ChannelHandler} wants to write data back, it queues the data in pending write queue and + * clears it in block manner. This provides better throughput. + */ +public abstract class AbstractNioChannel { + + private final SelectableChannel channel; + private final ChannelHandler handler; + private final Map> channelToPendingWrites = + new ConcurrentHashMap<>(); + private NioReactor reactor; + + /** + * Creates a new channel. + * + * @param handler which will handle events occurring on this channel. + * @param channel a NIO channel to be wrapped. + */ + public AbstractNioChannel(ChannelHandler handler, SelectableChannel channel) { + this.handler = handler; + this.channel = channel; + } + + /** + * Injects the reactor in this channel. + */ + void setReactor(NioReactor reactor) { + this.reactor = reactor; + } + + /** + * @return the wrapped NIO channel. + */ + public SelectableChannel getJavaChannel() { + return channel; + } + + /** + * The operation in which the channel is interested, this operation is provided to + * {@link Selector}. + * + * @return interested operation. + * @see SelectionKey + */ + public abstract int getInterestedOps(); + + /** + * Binds the channel on provided port. + * + * @throws IOException if any I/O error occurs. + */ + public abstract void bind() throws IOException; + + /** + * Reads the data using the key and returns the read data. The underlying channel should be + * fetched using {@link SelectionKey#channel()}. + * + * @param key the key on which read event occurred. + * @return data read. + * @throws IOException if any I/O error occurs. + */ + public abstract Object read(SelectionKey key) throws IOException; + + /** + * @return the handler associated with this channel. + */ + public ChannelHandler getHandler() { + return handler; + } + + /* + * Called from the context of reactor thread when the key becomes writable. The channel writes the + * whole pending block of data at once. + */ + void flush(SelectionKey key) throws IOException { + Queue pendingWrites = channelToPendingWrites.get(key.channel()); + while (true) { + Object pendingWrite = pendingWrites.poll(); + if (pendingWrite == null) { + // We don't have anything more to write so channel is interested in reading more data + reactor.changeOps(key, SelectionKey.OP_READ); + break; + } + + // ask the concrete channel to make sense of data and write it to java channel + doWrite(pendingWrite, key); + } + } + + /** + * Writes the data to the channel. + * + * @param pendingWrite the data to be written on channel. + * @param key the key which is writable. + * @throws IOException if any I/O error occurs. + */ + protected abstract void doWrite(Object pendingWrite, SelectionKey key) throws IOException; + + /** + * Queues the data for writing. The data is not guaranteed to be written on underlying channel + * when this method returns. It will be written when the channel is flushed. + * + *

+ * This method is used by the {@link ChannelHandler} to send reply back to the client.
+ * Example: + * + *

+   * 
+   * {@literal @}Override
+   * public void handleChannelRead(AbstractNioChannel channel, Object readObject, SelectionKey key) {
+   *   byte[] data = ((ByteBuffer)readObject).array();
+   *   ByteBuffer buffer = ByteBuffer.wrap("Server reply".getBytes());
+   *   channel.write(buffer, key);
+   * }
+   * 
+   * 
+ * + * @param data the data to be written on underlying channel. + * @param key the key which is writable. + */ + public void write(Object data, SelectionKey key) { + Queue pendingWrites = this.channelToPendingWrites.get(key.channel()); + if (pendingWrites == null) { + synchronized (this.channelToPendingWrites) { + pendingWrites = this.channelToPendingWrites.get(key.channel()); + if (pendingWrites == null) { + pendingWrites = new ConcurrentLinkedQueue<>(); + this.channelToPendingWrites.put(key.channel(), pendingWrites); + } + } + } + pendingWrites.add(data); + reactor.changeOps(key, SelectionKey.OP_WRITE); + } +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/ChannelHandler.java b/reactor/src/main/java/com/iluwatar/reactor/framework/ChannelHandler.java new file mode 100644 index 000000000..86b30b0dd --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/ChannelHandler.java @@ -0,0 +1,45 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.reactor.framework; + +import java.nio.channels.SelectionKey; + +/** + * Represents the EventHandler of Reactor pattern. It handles the incoming events dispatched + * to it by the {@link Dispatcher}. This is where the application logic resides. + * + *

+ * A {@link ChannelHandler} can be associated with one or many {@link AbstractNioChannel}s, and + * whenever an event occurs on any of the associated channels, the handler is notified of the event. + */ +public interface ChannelHandler { + + /** + * Called when the {@code channel} receives some data from remote peer. + * + * @param channel the channel from which the data was received. + * @param readObject the data read. + * @param key the key on which read event occurred. + */ + void handleChannelRead(AbstractNioChannel channel, Object readObject, SelectionKey key); +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/Dispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/framework/Dispatcher.java new file mode 100644 index 000000000..d9c93190a --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/Dispatcher.java @@ -0,0 +1,63 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.reactor.framework; + +import java.nio.channels.SelectionKey; + +/** + * Represents the event dispatching strategy. When {@link NioReactor} senses any event on the + * registered {@link AbstractNioChannel}s then it de-multiplexes the event type, read or write or + * connect, and then calls the {@link Dispatcher} to dispatch the read events. This decouples the + * I/O processing from application specific processing.
+ * Dispatcher should call the {@link ChannelHandler} associated with the channel on which event + * occurred. + * + *

+ * The application can customize the way in which event is dispatched such as using the reactor + * thread to dispatch event to channels or use a worker pool to do the non I/O processing. + * + * @see SameThreadDispatcher + * @see ThreadPoolDispatcher + */ +public interface Dispatcher { + /** + * This hook method is called when read event occurs on particular channel. The data read is + * provided in readObject. The implementation should dispatch this read event to the + * associated {@link ChannelHandler} of channel. + * + *

+ * The type of readObject depends on the channel on which data was received. + * + * @param channel on which read event occurred + * @param readObject object read by channel + * @param key on which event occurred + */ + void onChannelReadEvent(AbstractNioChannel channel, Object readObject, SelectionKey key); + + /** + * Stops dispatching events and cleans up any acquired resources such as threads. + * + * @throws InterruptedException if interrupted while stopping dispatcher. + */ + void stop() throws InterruptedException; +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java b/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java new file mode 100644 index 000000000..4e493163f --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java @@ -0,0 +1,181 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.reactor.framework; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.DatagramChannel; +import java.nio.channels.SelectionKey; + +/** + * A wrapper over {@link DatagramChannel} which can read and write data on a DatagramChannel. + */ +public class NioDatagramChannel extends AbstractNioChannel { + + private final int port; + + /** + * Creates a {@link DatagramChannel} which will bind at provided port and use handler + * to handle incoming events on this channel. + *

+ * Note the constructor does not bind the socket, {@link #bind()} method should be called for + * binding the socket. + * + * @param port the port to be bound to listen for incoming datagram requests. + * @param handler the handler to be used for handling incoming requests on this channel. + * @throws IOException if any I/O error occurs. + */ + public NioDatagramChannel(int port, ChannelHandler handler) throws IOException { + super(handler, DatagramChannel.open()); + this.port = port; + } + + @Override + public int getInterestedOps() { + /* + * there is no need to accept connections in UDP, so the channel shows interest in reading data. + */ + return SelectionKey.OP_READ; + } + + /** + * Reads and returns a {@link DatagramPacket} from the underlying channel. + * + * @return the datagram packet read having the sender address. + */ + @Override + public DatagramPacket read(SelectionKey key) throws IOException { + ByteBuffer buffer = ByteBuffer.allocate(1024); + SocketAddress sender = ((DatagramChannel) key.channel()).receive(buffer); + + /* + * It is required to create a DatagramPacket because we need to preserve which socket address + * acts as destination for sending reply packets. + */ + buffer.flip(); + DatagramPacket packet = new DatagramPacket(buffer); + packet.setSender(sender); + + return packet; + } + + /** + * @return the underlying datagram channel. + */ + @Override + public DatagramChannel getJavaChannel() { + return (DatagramChannel) super.getJavaChannel(); + } + + /** + * Binds UDP socket on the provided port. + * + * @throws IOException if any I/O error occurs. + */ + @Override + public void bind() throws IOException { + getJavaChannel().socket().bind(new InetSocketAddress(InetAddress.getLocalHost(), port)); + getJavaChannel().configureBlocking(false); + System.out.println("Bound UDP socket at port: " + port); + } + + /** + * Writes the pending {@link DatagramPacket} to the underlying channel sending data to the + * intended receiver of the packet. + */ + @Override + protected void doWrite(Object pendingWrite, SelectionKey key) throws IOException { + DatagramPacket pendingPacket = (DatagramPacket) pendingWrite; + getJavaChannel().send(pendingPacket.getData(), pendingPacket.getReceiver()); + } + + /** + * Writes the outgoing {@link DatagramPacket} to the channel. The intended receiver of the + * datagram packet must be set in the data using + * {@link DatagramPacket#setReceiver(SocketAddress)}. + */ + @Override + public void write(Object data, SelectionKey key) { + super.write(data, key); + } + + /** + * Container of data used for {@link NioDatagramChannel} to communicate with remote peer. + */ + public static class DatagramPacket { + private SocketAddress sender; + private ByteBuffer data; + private SocketAddress receiver; + + /** + * Creates a container with underlying data. + * + * @param data the underlying message to be written on channel. + */ + public DatagramPacket(ByteBuffer data) { + this.data = data; + } + + /** + * @return the sender address. + */ + public SocketAddress getSender() { + return sender; + } + + /** + * Sets the sender address of this packet. + * + * @param sender the sender address. + */ + public void setSender(SocketAddress sender) { + this.sender = sender; + } + + /** + * @return the receiver address. + */ + public SocketAddress getReceiver() { + return receiver; + } + + /** + * Sets the intended receiver address. This must be set when writing to the channel. + * + * @param receiver the receiver address. + */ + public void setReceiver(SocketAddress receiver) { + this.receiver = receiver; + } + + /** + * @return the underlying message that will be written on channel. + */ + public ByteBuffer getData() { + return data; + } + } +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java b/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java new file mode 100644 index 000000000..3d5ebf5a0 --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java @@ -0,0 +1,261 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.reactor.framework; + +import java.io.IOException; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.Iterator; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * This class acts as Synchronous Event De-multiplexer and Initiation Dispatcher of Reactor pattern. Multiple handles + * i.e. {@link AbstractNioChannel}s can be registered to the reactor and it blocks for events from all these handles. + * Whenever an event occurs on any of the registered handles, it synchronously de-multiplexes the event which can be any + * of read, write or accept, and dispatches the event to the appropriate {@link ChannelHandler} using the + * {@link Dispatcher}. + * + *

+ * Implementation: A NIO reactor runs in its own thread when it is started using {@link #start()} method. + * {@link NioReactor} uses {@link Selector} for realizing Synchronous Event De-multiplexing. + * + *

+ * NOTE: This is one of the ways to implement NIO reactor and it does not take care of all possible edge cases which are + * required in a real application. This implementation is meant to demonstrate the fundamental concepts that lie behind + * Reactor pattern. + */ +public class NioReactor { + + private final Selector selector; + private final Dispatcher dispatcher; + /** + * All the work of altering the SelectionKey operations and Selector operations are performed in the context of main + * event loop of reactor. So when any channel needs to change its readability or writability, a new command is added + * in the command queue and then the event loop picks up the command and executes it in next iteration. + */ + private final Queue pendingCommands = new ConcurrentLinkedQueue<>(); + private final ExecutorService reactorMain = Executors.newSingleThreadExecutor(); + + /** + * Creates a reactor which will use provided {@code dispatcher} to dispatch events. The application can provide + * various implementations of dispatcher which suits its needs. + * + * @param dispatcher + * a non-null dispatcher used to dispatch events on registered channels. + * @throws IOException + * if any I/O error occurs. + */ + public NioReactor(Dispatcher dispatcher) throws IOException { + this.dispatcher = dispatcher; + this.selector = Selector.open(); + } + + /** + * Starts the reactor event loop in a new thread. + * + * @throws IOException + * if any I/O error occurs. + */ + public void start() throws IOException { + reactorMain.execute(() -> { + try { + System.out.println("Reactor started, waiting for events..."); + eventLoop(); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } + + /** + * Stops the reactor and related resources such as dispatcher. + * + * @throws InterruptedException + * if interrupted while stopping the reactor. + * @throws IOException + * if any I/O error occurs. + */ + public void stop() throws InterruptedException, IOException { + reactorMain.shutdownNow(); + selector.wakeup(); + reactorMain.awaitTermination(4, TimeUnit.SECONDS); + selector.close(); + } + + /** + * Registers a new channel (handle) with this reactor. Reactor will start waiting for events on this channel and + * notify of any events. While registering the channel the reactor uses {@link AbstractNioChannel#getInterestedOps()} + * to know about the interested operation of this channel. + * + * @param channel + * a new channel on which reactor will wait for events. The channel must be bound prior to being registered. + * @return this + * @throws IOException + * if any I/O error occurs. + */ + public NioReactor registerChannel(AbstractNioChannel channel) throws IOException { + SelectionKey key = channel.getJavaChannel().register(selector, channel.getInterestedOps()); + key.attach(channel); + channel.setReactor(this); + return this; + } + + private void eventLoop() throws IOException { + while (true) { + + // honor interrupt request + if (Thread.interrupted()) { + break; + } + + // honor any pending commands first + processPendingCommands(); + + /* + * Synchronous event de-multiplexing happens here, this is blocking call which returns when it is possible to + * initiate non-blocking operation on any of the registered channels. + */ + selector.select(); + + /* + * Represents the events that have occurred on registered handles. + */ + Set keys = selector.selectedKeys(); + + Iterator iterator = keys.iterator(); + + while (iterator.hasNext()) { + SelectionKey key = iterator.next(); + if (!key.isValid()) { + iterator.remove(); + continue; + } + processKey(key); + } + keys.clear(); + } + } + + private void processPendingCommands() { + Iterator iterator = pendingCommands.iterator(); + while (iterator.hasNext()) { + Runnable command = iterator.next(); + command.run(); + iterator.remove(); + } + } + + /* + * Initiation dispatcher logic, it checks the type of event and notifier application specific event handler to handle + * the event. + */ + private void processKey(SelectionKey key) throws IOException { + if (key.isAcceptable()) { + onChannelAcceptable(key); + } else if (key.isReadable()) { + onChannelReadable(key); + } else if (key.isWritable()) { + onChannelWritable(key); + } + } + + private static void onChannelWritable(SelectionKey key) throws IOException { + AbstractNioChannel channel = (AbstractNioChannel) key.attachment(); + channel.flush(key); + } + + private void onChannelReadable(SelectionKey key) { + try { + // reads the incoming data in context of reactor main loop. Can this be improved? + Object readObject = ((AbstractNioChannel) key.attachment()).read(key); + + dispatchReadEvent(key, readObject); + } catch (IOException e) { + try { + key.channel().close(); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + } + + /* + * Uses the application provided dispatcher to dispatch events to application handler. + */ + private void dispatchReadEvent(SelectionKey key, Object readObject) { + dispatcher.onChannelReadEvent((AbstractNioChannel) key.attachment(), readObject, key); + } + + private void onChannelAcceptable(SelectionKey key) throws IOException { + ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel(); + SocketChannel socketChannel = serverSocketChannel.accept(); + socketChannel.configureBlocking(false); + SelectionKey readKey = socketChannel.register(selector, SelectionKey.OP_READ); + readKey.attach(key.attachment()); + } + + /** + * Queues the change of operations request of a channel, which will change the interested operations of the channel + * sometime in future. + *

+ * This is a non-blocking method and does not guarantee that the operations have changed when this method returns. + * + * @param key + * the key for which operations have to be changed. + * @param interestedOps + * the new interest operations. + */ + public void changeOps(SelectionKey key, int interestedOps) { + pendingCommands.add(new ChangeKeyOpsCommand(key, interestedOps)); + selector.wakeup(); + } + + /** + * A command that changes the interested operations of the key provided. + */ + class ChangeKeyOpsCommand implements Runnable { + private SelectionKey key; + private int interestedOps; + + public ChangeKeyOpsCommand(SelectionKey key, int interestedOps) { + this.key = key; + this.interestedOps = interestedOps; + } + + public void run() { + key.interestOps(interestedOps); + } + + @Override + public String toString() { + return "Change of ops to: " + interestedOps; + } + } +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java b/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java new file mode 100644 index 000000000..c7d67fd13 --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java @@ -0,0 +1,111 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.reactor.framework; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; + +/** + * A wrapper over {@link NioServerSocketChannel} which can read and write data on a + * {@link SocketChannel}. + */ +public class NioServerSocketChannel extends AbstractNioChannel { + + private final int port; + + /** + * Creates a {@link ServerSocketChannel} which will bind at provided port and use + * handler to handle incoming events on this channel. + *

+ * Note the constructor does not bind the socket, {@link #bind()} method should be called for + * binding the socket. + * + * @param port the port on which channel will be bound to accept incoming connection requests. + * @param handler the handler that will handle incoming requests on this channel. + * @throws IOException if any I/O error occurs. + */ + public NioServerSocketChannel(int port, ChannelHandler handler) throws IOException { + super(handler, ServerSocketChannel.open()); + this.port = port; + } + + + @Override + public int getInterestedOps() { + // being a server socket channel it is interested in accepting connection from remote peers. + return SelectionKey.OP_ACCEPT; + } + + /** + * @return the underlying {@link ServerSocketChannel}. + */ + @Override + public ServerSocketChannel getJavaChannel() { + return (ServerSocketChannel) super.getJavaChannel(); + } + + /** + * Reads and returns {@link ByteBuffer} from the underlying {@link SocketChannel} represented by + * the key. Due to the fact that there is a dedicated channel for each client + * connection we don't need to store the sender. + */ + @Override + public ByteBuffer read(SelectionKey key) throws IOException { + SocketChannel socketChannel = (SocketChannel) key.channel(); + ByteBuffer buffer = ByteBuffer.allocate(1024); + int read = socketChannel.read(buffer); + buffer.flip(); + if (read == -1) { + throw new IOException("Socket closed"); + } + return buffer; + } + + /** + * Binds TCP socket on the provided port. + * + * @throws IOException if any I/O error occurs. + */ + @Override + public void bind() throws IOException { + ((ServerSocketChannel) getJavaChannel()).socket().bind( + new InetSocketAddress(InetAddress.getLocalHost(), port)); + ((ServerSocketChannel) getJavaChannel()).configureBlocking(false); + System.out.println("Bound TCP socket at port: " + port); + } + + /** + * Writes the pending {@link ByteBuffer} to the underlying channel sending data to the intended + * receiver of the packet. + */ + @Override + protected void doWrite(Object pendingWrite, SelectionKey key) throws IOException { + ByteBuffer pendingBuffer = (ByteBuffer) pendingWrite; + ((SocketChannel) key.channel()).write(pendingBuffer); + } +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/SameThreadDispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/framework/SameThreadDispatcher.java new file mode 100644 index 000000000..855d8b090 --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/SameThreadDispatcher.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.reactor.framework; + +import java.nio.channels.SelectionKey; + +/** + * Dispatches the events in the context of caller thread. This implementation is a good fit for + * small applications where there are limited clients. Using this implementation limits the + * scalability because the I/O thread performs the application specific processing. + * + *

+ * For better performance use {@link ThreadPoolDispatcher}. + * + * @see ThreadPoolDispatcher + */ +public class SameThreadDispatcher implements Dispatcher { + + /** + * Dispatches the read event in the context of caller thread.
+ * Note this is a blocking call. It returns only after the associated handler has handled the read + * event. + */ + @Override + public void onChannelReadEvent(AbstractNioChannel channel, Object readObject, SelectionKey key) { + /* + * Calls the associated handler to notify the read event where application specific code + * resides. + */ + channel.getHandler().handleChannelRead(channel, readObject, key); + } + + /** + * No resources to free. + */ + @Override + public void stop() { + // no-op + } +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/ThreadPoolDispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/framework/ThreadPoolDispatcher.java new file mode 100644 index 000000000..0ca114abe --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/ThreadPoolDispatcher.java @@ -0,0 +1,69 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.reactor.framework; + +import java.nio.channels.SelectionKey; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * An implementation that uses a pool of worker threads to dispatch the events. This provides better + * scalability as the application specific processing is not performed in the context of I/O + * (reactor) thread. + */ +public class ThreadPoolDispatcher implements Dispatcher { + + private final ExecutorService executorService; + + /** + * Creates a pooled dispatcher with tunable pool size. + * + * @param poolSize number of pooled threads + */ + public ThreadPoolDispatcher(int poolSize) { + this.executorService = Executors.newFixedThreadPool(poolSize); + } + + /** + * Submits the work of dispatching the read event to worker pool, where it gets picked up by + * worker threads.
+ * Note that this is a non-blocking call and returns immediately. It is not guaranteed that the + * event has been handled by associated handler. + */ + @Override + public void onChannelReadEvent(AbstractNioChannel channel, Object readObject, SelectionKey key) { + executorService.execute(() -> channel.getHandler().handleChannelRead(channel, readObject, key)); + } + + /** + * Stops the pool of workers. + * + * @throws InterruptedException if interrupted while stopping pool of workers. + */ + @Override + public void stop() throws InterruptedException { + executorService.shutdown(); + executorService.awaitTermination(4, TimeUnit.SECONDS); + } +} diff --git a/reactor/src/test/java/com/iluwatar/reactor/app/ReactorTest.java b/reactor/src/test/java/com/iluwatar/reactor/app/ReactorTest.java new file mode 100644 index 000000000..1ec70f87f --- /dev/null +++ b/reactor/src/test/java/com/iluwatar/reactor/app/ReactorTest.java @@ -0,0 +1,90 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.reactor.app; + +import java.io.IOException; + +import org.junit.Test; + +import com.iluwatar.reactor.framework.SameThreadDispatcher; +import com.iluwatar.reactor.framework.ThreadPoolDispatcher; + +/** + * + * This class tests the Distributed Logging service by starting a Reactor and then sending it + * concurrent logging requests using multiple clients. + */ +public class ReactorTest { + + /** + * Test the application using pooled thread dispatcher. + * + * @throws IOException if any I/O error occurs. + * @throws InterruptedException if interrupted while stopping the application. + */ + @Test + public void testAppUsingThreadPoolDispatcher() throws IOException, InterruptedException { + App app = new App(new ThreadPoolDispatcher(2)); + app.start(); + + AppClient client = new AppClient(); + client.start(); + + // allow clients to send requests. Artificial delay. + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + client.stop(); + + app.stop(); + } + + /** + * Test the application using same thread dispatcher. + * + * @throws IOException if any I/O error occurs. + * @throws InterruptedException if interrupted while stopping the application. + */ + @Test + public void testAppUsingSameThreadDispatcher() throws IOException, InterruptedException { + App app = new App(new SameThreadDispatcher()); + app.start(); + + AppClient client = new AppClient(); + client.start(); + + // allow clients to send requests. Artificial delay. + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + client.stop(); + + app.stop(); + } +} diff --git a/reader-writer-lock/README.md b/reader-writer-lock/README.md new file mode 100644 index 000000000..40b711361 --- /dev/null +++ b/reader-writer-lock/README.md @@ -0,0 +1,32 @@ +--- +layout: pattern +title: Reader Writer Lock +folder: reader-writer-lock +permalink: /patterns/reader-writer-lock/ +categories: Concurrency +tags: + - Java + - Difficulty-Intermediate + - I/O + - Performance +--- + +**Intent:** + +Suppose we have a shared memory area with the basic constraints detailed above. It is possible to protect the shared data behind a mutual exclusion mutex, in which case no two threads can access the data at the same time. However, this solution is suboptimal, because it is possible that a reader R1 might have the lock, and then another reader R2 requests access. It would be foolish for R2 to wait until R1 was done before starting its own read operation; instead, R2 should start right away. This is the motivation for the Reader Writer Lock pattern. + +![alt text](./etc/reader-writer-lock.png "Reader writer lock") + +**Applicability:** + +Application need to increase the performance of resource synchronize for multiple thread, in particularly there are mixed read/write operations. + +**Real world examples:** + +* [Java Reader Writer Lock](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReadWriteLock.html) + +**Credits** + +* [Readers–writer lock](https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock) + +* [Readers–writers_problem](https://en.wikipedia.org/wiki/Readers%E2%80%93writers_problem) \ No newline at end of file diff --git a/reader-writer-lock/etc/reader-writer-lock.png b/reader-writer-lock/etc/reader-writer-lock.png new file mode 100644 index 000000000..f7b600530 Binary files /dev/null and b/reader-writer-lock/etc/reader-writer-lock.png differ diff --git a/reader-writer-lock/etc/reader-writer-lock.ucls b/reader-writer-lock/etc/reader-writer-lock.ucls new file mode 100644 index 000000000..920904e76 --- /dev/null +++ b/reader-writer-lock/etc/reader-writer-lock.ucls @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/reader-writer-lock/pom.xml b/reader-writer-lock/pom.xml new file mode 100644 index 000000000..2df3d5b14 --- /dev/null +++ b/reader-writer-lock/pom.xml @@ -0,0 +1,49 @@ + + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + reader-writer-lock + + + junit + junit + test + + + org.mockito + mockito-core + test + + + + diff --git a/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/App.java b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/App.java new file mode 100644 index 000000000..fd5d28ed5 --- /dev/null +++ b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/App.java @@ -0,0 +1,79 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.reader.writer.lock; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; + +/** + * + * In a multiple thread applications, the threads may try to synchronize the shared resources + * regardless of read or write operation. It leads to a low performance especially in a "read more + * write less" system as indeed the read operations are thread-safe to another read operation. + *

+ * Reader writer lock is a synchronization primitive that try to resolve this problem. This pattern + * allows concurrent access for read-only operations, while write operations require exclusive + * access. This means that multiple threads can read the data in parallel but an exclusive lock is + * needed for writing or modifying data. When a writer is writing the data, all other writers or + * readers will be blocked until the writer is finished writing. + * + *

+ * This example use two mutex to demonstrate the concurrent access of multiple readers and writers. + * + * + * @author hongshuwei@gmail.com + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + + ExecutorService executeService = Executors.newFixedThreadPool(10); + ReaderWriterLock lock = new ReaderWriterLock(); + + // Start 5 readers + IntStream.range(0, 5) + .forEach(i -> executeService.submit(new Reader("Reader " + i, lock.readLock()))); + + // Start 5 writers + IntStream.range(0, 5) + .forEach(i -> executeService.submit(new Writer("Writer " + i, lock.writeLock()))); + // In the system console, it can see that the read operations are executed concurrently while + // write operations are exclusive. + executeService.shutdown(); + try { + executeService.awaitTermination(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + System.out.println("Error waiting for ExecutorService shutdown"); + } + + } + +} diff --git a/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Reader.java b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Reader.java new file mode 100644 index 000000000..2b837b341 --- /dev/null +++ b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Reader.java @@ -0,0 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.reader.writer.lock; + +import java.util.concurrent.locks.Lock; + +/** + * Reader class, read when it acquired the read lock + */ +public class Reader implements Runnable { + + private Lock readLock; + + private String name; + + public Reader(String name, Lock readLock) { + this.name = name; + this.readLock = readLock; + } + + @Override + public void run() { + readLock.lock(); + try { + read(); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + readLock.unlock(); + } + } + + /** + * Simulate the read operation + * + */ + public void read() throws InterruptedException { + System.out.println(name + " begin"); + Thread.sleep(250); + System.out.println(name + " finish"); + } +} diff --git a/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/ReaderWriterLock.java b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/ReaderWriterLock.java new file mode 100644 index 000000000..f08ed805d --- /dev/null +++ b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/ReaderWriterLock.java @@ -0,0 +1,233 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.reader.writer.lock; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; + +/** + * Class responsible for control the access for reader or writer + * + * Allows multiple readers to hold the lock at same time, but if any writer holds the lock then + * readers wait. If reader holds the lock then writer waits. This lock is not fair. + */ +public class ReaderWriterLock implements ReadWriteLock { + + + private Object readerMutex = new Object(); + + private int currentReaderCount; + + /** + * Global mutex is used to indicate that whether reader or writer gets the lock in the moment. + *

+ * 1. When it contains the reference of {@link readerLock}, it means that the lock is acquired by + * the reader, another reader can also do the read operation concurrently.
+ * 2. When it contains the reference of reference of {@link writerLock}, it means that the lock is + * acquired by the writer exclusively, no more reader or writer can get the lock. + *

+ * This is the most important field in this class to control the access for reader/writer. + */ + private Set globalMutex = new HashSet<>(); + + private ReadLock readerLock = new ReadLock(); + private WriteLock writerLock = new WriteLock(); + + @Override + public Lock readLock() { + return readerLock; + } + + @Override + public Lock writeLock() { + return writerLock; + } + + /** + * return true when globalMutex hold the reference of writerLock + */ + private boolean doesWriterOwnThisLock() { + return globalMutex.contains(writerLock); + } + + /** + * return true when globalMutex hold the reference of readerLock + */ + private boolean doesReaderOwnThisLock() { + return globalMutex.contains(readerLock); + } + + /** + * Nobody get the lock when globalMutex contains nothing + * + */ + private boolean isLockFree() { + return globalMutex.isEmpty(); + } + + private static void waitUninterruptibly(Object o) { + try { + o.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + /** + * Reader Lock, can be access for more than one reader concurrently if no writer get the lock + */ + private class ReadLock implements Lock { + + @Override + public void lock() { + + synchronized (readerMutex) { + + currentReaderCount++; + if (currentReaderCount == 1) { + // Try to get the globalMutex lock for the first reader + synchronized (globalMutex) { + while (true) { + // If the no one get the lock or the lock is locked by reader, just set the reference + // to the globalMutex to indicate that the lock is locked by Reader. + if (isLockFree() || doesReaderOwnThisLock()) { + globalMutex.add(this); + break; + } else { + // If lock is acquired by the write, let the thread wait until the writer release + // the lock + waitUninterruptibly(globalMutex); + } + } + } + + } + } + } + + @Override + public void unlock() { + + synchronized (readerMutex) { + currentReaderCount--; + // Release the lock only when it is the last reader, it is ensure that the lock is released + // when all reader is completely. + if (currentReaderCount == 0) { + synchronized (globalMutex) { + // Notify the waiter, mostly the writer + globalMutex.remove(this); + globalMutex.notifyAll(); + } + } + } + + } + + @Override + public void lockInterruptibly() throws InterruptedException { + throw new UnsupportedOperationException(); + } + + @Override + public boolean tryLock() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { + throw new UnsupportedOperationException(); + } + + @Override + public Condition newCondition() { + throw new UnsupportedOperationException(); + } + + } + + /** + * Writer Lock, can only be accessed by one writer concurrently + */ + private class WriteLock implements Lock { + + @Override + public void lock() { + + synchronized (globalMutex) { + + while (true) { + // When there is no one acquired the lock, just put the writeLock reference to the + // globalMutex to indicate that the lock is acquired by one writer. + // It is ensure that writer can only get the lock when no reader/writer acquired the lock. + if (isLockFree()) { + globalMutex.add(this); + break; + } else if (doesWriterOwnThisLock()) { + // Wait when other writer get the lock + waitUninterruptibly(globalMutex); + } else if (doesReaderOwnThisLock()) { + // Wait when other reader get the lock + waitUninterruptibly(globalMutex); + } else { + throw new AssertionError("it should never reach here"); + } + } + } + } + + @Override + public void unlock() { + + synchronized (globalMutex) { + globalMutex.remove(this); + // Notify the waiter, other writer or reader + globalMutex.notifyAll(); + } + } + + @Override + public void lockInterruptibly() throws InterruptedException { + throw new UnsupportedOperationException(); + } + + @Override + public boolean tryLock() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { + throw new UnsupportedOperationException(); + } + + @Override + public Condition newCondition() { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Writer.java b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Writer.java new file mode 100644 index 000000000..fc3c3bb88 --- /dev/null +++ b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Writer.java @@ -0,0 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.reader.writer.lock; + +import java.util.concurrent.locks.Lock; + +/** + * Writer class, write when it acquired the write lock + */ +public class Writer implements Runnable { + + private Lock writeLock; + + private String name; + + public Writer(String name, Lock writeLock) { + this.name = name; + this.writeLock = writeLock; + } + + + @Override + public void run() { + writeLock.lock(); + try { + write(); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + writeLock.unlock(); + } + } + + /** + * Simulate the write operation + */ + public void write() throws InterruptedException { + System.out.println(name + " begin"); + Thread.sleep(250); + System.out.println(name + " finish"); + } +} diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/AppTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/AppTest.java new file mode 100644 index 000000000..09c591a48 --- /dev/null +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/AppTest.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.reader.writer.lock; + +import org.junit.Test; + +/** + * Application test + */ +public class AppTest { + + @Test + public void test() throws Exception { + String[] args = {}; + App.main(args); + + } +} diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderAndWriterTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderAndWriterTest.java new file mode 100644 index 000000000..a2496a3c0 --- /dev/null +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderAndWriterTest.java @@ -0,0 +1,104 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.reader.writer.lock; + +import static org.mockito.Mockito.inOrder; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; +import org.mockito.InOrder; + +/** + * @author hongshuwei@gmail.com + */ +public class ReaderAndWriterTest extends StdOutTest { + + + + /** + * Verify reader and writer can only get the lock to read and write orderly + */ + @Test + public void testReadAndWrite() throws Exception { + + ReaderWriterLock lock = new ReaderWriterLock(); + + Reader reader1 = new Reader("Reader 1", lock.readLock()); + Writer writer1 = new Writer("Writer 1", lock.writeLock()); + + ExecutorService executeService = Executors.newFixedThreadPool(2); + executeService.submit(reader1); + // Let reader1 execute first + Thread.sleep(150); + executeService.submit(writer1); + + executeService.shutdown(); + try { + executeService.awaitTermination(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + System.out.println("Error waiting for ExecutorService shutdown"); + } + + final InOrder inOrder = inOrder(getStdOutMock()); + inOrder.verify(getStdOutMock()).println("Reader 1 begin"); + inOrder.verify(getStdOutMock()).println("Reader 1 finish"); + inOrder.verify(getStdOutMock()).println("Writer 1 begin"); + inOrder.verify(getStdOutMock()).println("Writer 1 finish"); + } + + /** + * Verify reader and writer can only get the lock to read and write orderly + */ + @Test + public void testWriteAndRead() throws Exception { + + ExecutorService executeService = Executors.newFixedThreadPool(2); + ReaderWriterLock lock = new ReaderWriterLock(); + + Reader reader1 = new Reader("Reader 1", lock.readLock()); + Writer writer1 = new Writer("Writer 1", lock.writeLock()); + + executeService.submit(writer1); + // Let writer1 execute first + Thread.sleep(150); + executeService.submit(reader1); + + executeService.shutdown(); + try { + executeService.awaitTermination(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + System.out.println("Error waiting for ExecutorService shutdown"); + } + + final InOrder inOrder = inOrder(getStdOutMock()); + inOrder.verify(getStdOutMock()).println("Writer 1 begin"); + inOrder.verify(getStdOutMock()).println("Writer 1 finish"); + inOrder.verify(getStdOutMock()).println("Reader 1 begin"); + inOrder.verify(getStdOutMock()).println("Reader 1 finish"); + } +} + diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderTest.java new file mode 100644 index 000000000..7d51e977c --- /dev/null +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderTest.java @@ -0,0 +1,72 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.reader.writer.lock; + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.spy; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; +import org.mockito.InOrder; + +/** + * @author hongshuwei@gmail.com + */ +public class ReaderTest extends StdOutTest { + + /** + * Verify that multiple readers can get the read lock concurrently + */ + @Test + public void testRead() throws Exception { + + ExecutorService executeService = Executors.newFixedThreadPool(2); + ReaderWriterLock lock = new ReaderWriterLock(); + + Reader reader1 = spy(new Reader("Reader 1", lock.readLock())); + Reader reader2 = spy(new Reader("Reader 2", lock.readLock())); + + executeService.submit(reader1); + Thread.sleep(150); + executeService.submit(reader2); + + executeService.shutdown(); + try { + executeService.awaitTermination(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + System.out.println("Error waiting for ExecutorService shutdown"); + } + + // Read operation will hold the read lock 250 milliseconds, so here we prove that multiple reads + // can be performed in the same time. + final InOrder inOrder = inOrder(getStdOutMock()); + inOrder.verify(getStdOutMock()).println("Reader 1 begin"); + inOrder.verify(getStdOutMock()).println("Reader 2 begin"); + inOrder.verify(getStdOutMock()).println("Reader 1 finish"); + inOrder.verify(getStdOutMock()).println("Reader 2 finish"); + + } +} diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/StdOutTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/StdOutTest.java new file mode 100644 index 000000000..7a1af09c0 --- /dev/null +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/StdOutTest.java @@ -0,0 +1,76 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.reader.writer.lock; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since some actions don't have any + * influence on accessible objects, except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/WriterTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/WriterTest.java new file mode 100644 index 000000000..765c491ff --- /dev/null +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/WriterTest.java @@ -0,0 +1,72 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.reader.writer.lock; + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.spy; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; +import org.mockito.InOrder; + +/** + * @author hongshuwei@gmail.com + */ +public class WriterTest extends StdOutTest { + + /** + * Verify that multiple writers will get the lock in order. + */ + @Test + public void testWrite() throws Exception { + + ExecutorService executeService = Executors.newFixedThreadPool(2); + ReaderWriterLock lock = new ReaderWriterLock(); + + Writer writer1 = spy(new Writer("Writer 1", lock.writeLock())); + Writer writer2 = spy(new Writer("Writer 2", lock.writeLock())); + + executeService.submit(writer1); + // Let write1 execute first + Thread.sleep(150); + executeService.submit(writer2); + + executeService.shutdown(); + try { + executeService.awaitTermination(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + System.out.println("Error waiting for ExecutorService shutdown"); + } + // Write operation will hold the write lock 250 milliseconds, so here we verify that when two + // writer execute concurrently, the second writer can only writes only when the first one is + // finished. + final InOrder inOrder = inOrder(getStdOutMock()); + inOrder.verify(getStdOutMock()).println("Writer 1 begin"); + inOrder.verify(getStdOutMock()).println("Writer 1 finish"); + inOrder.verify(getStdOutMock()).println("Writer 2 begin"); + inOrder.verify(getStdOutMock()).println("Writer 2 finish"); + } +} diff --git a/repository/README.md b/repository/README.md new file mode 100644 index 000000000..67b3ea44e --- /dev/null +++ b/repository/README.md @@ -0,0 +1,37 @@ +--- +layout: pattern +title: Repository +folder: repository +permalink: /patterns/repository/ +categories: Persistence Tier +tags: + - Java + - Difficulty-Intermediate + - Spring +--- + +## Intent +Repository layer is added between the domain and data mapping +layers to isolate domain objects from details of the database access code and +to minimize scattering and duplication of query code. The Repository pattern is +especially useful in systems where number of domain classes is large or heavy +querying is utilized. + +![alt text](./etc/repository.png "Repository") + +## Applicability +Use the Repository pattern when + +* the number of domain objects is large +* you want to avoid duplication of query code +* you want to keep the database querying code in single place +* you have multiple data sources + +## Real world examples + +* [Spring Data](http://projects.spring.io/spring-data/) + +## Credits + +* [Don’t use DAO, use Repository](http://thinkinginobjects.com/2012/08/26/dont-use-dao-use-repository/) +* [Advanced Spring Data JPA - Specifications and Querydsl](https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/) diff --git a/repository/etc/repository.png b/repository/etc/repository.png new file mode 100644 index 000000000..08d5d571d Binary files /dev/null and b/repository/etc/repository.png differ diff --git a/repository/etc/repository.ucls b/repository/etc/repository.ucls new file mode 100644 index 000000000..894e9434c --- /dev/null +++ b/repository/etc/repository.ucls @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/repository/pom.xml b/repository/pom.xml new file mode 100644 index 000000000..56a448ec5 --- /dev/null +++ b/repository/pom.xml @@ -0,0 +1,66 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + repository + + + org.springframework + spring-test + + + org.springframework.data + spring-data-jpa + + + org.hibernate + hibernate-entitymanager + + + commons-dbcp + commons-dbcp + + + com.h2database + h2 + + + junit + junit + test + + + com.google.guava + guava + + + diff --git a/repository/src/main/java/com/iluwatar/repository/App.java b/repository/src/main/java/com/iluwatar/repository/App.java new file mode 100644 index 000000000..2807ae7ca --- /dev/null +++ b/repository/src/main/java/com/iluwatar/repository/App.java @@ -0,0 +1,108 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.repository; + +import java.util.List; + +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * Repository pattern mediates between the domain and data mapping layers using a collection-like + * interface for accessing domain objects. A system with complex domain model often benefits from a + * layer that isolates domain objects from the details of the database access code and in such + * systems it can be worthwhile to build another layer of abstraction over the mapping layer where + * query construction code is concentrated. This becomes more important when there are a large + * number of domain classes or heavy querying. In these cases particularly, adding this layer helps + * minimize duplicate query logic. + *

+ * In this example we utilize Spring Data to automatically generate a repository for us from the + * {@link Person} domain object. Using the {@link PersonRepository} we perform CRUD operations on + * the entity, moreover, the query by {@link org.springframework.data.jpa.domain.Specification} are + * also performed. Underneath we have configured in-memory H2 database for which schema is created + * and dropped on each run. + */ +public class App { + + /** + * Program entry point + * + * @param args + * command line args + */ + public static void main(String[] args) { + + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( + "applicationContext.xml"); + PersonRepository repository = context.getBean(PersonRepository.class); + + Person peter = new Person("Peter", "Sagan", 17); + Person nasta = new Person("Nasta", "Kuzminova", 25); + Person john = new Person("John", "lawrence", 35); + Person terry = new Person("Terry", "Law", 36); + + // Add new Person records + repository.save(peter); + repository.save(nasta); + repository.save(john); + repository.save(terry); + + // Count Person records + System.out.println("Count Person records: " + repository.count()); + + // Print all records + List persons = (List) repository.findAll(); + for (Person person : persons) { + System.out.println(person); + } + + // Update Person + nasta.setName("Barbora"); + nasta.setSurname("Spotakova"); + repository.save(nasta); + + System.out.println("Find by id 2: " + repository.findOne(2L)); + + // Remove record from Person + repository.delete(2L); + + // count records + System.out.println("Count Person records: " + repository.count()); + + // find by name + Person p = repository.findOne(new PersonSpecifications.NameEqualSpec("John")); + System.out.println("Find by John is " + p); + + // find by age + persons = repository.findAll(new PersonSpecifications.AgeBetweenSpec(20, 40)); + + System.out.println("Find Person with age between 20,40: "); + for (Person person : persons) { + System.out.println(person); + } + + repository.deleteAll(); + + context.close(); + + } +} diff --git a/repository/src/main/java/com/iluwatar/repository/AppConfig.java b/repository/src/main/java/com/iluwatar/repository/AppConfig.java new file mode 100644 index 000000000..3e7093358 --- /dev/null +++ b/repository/src/main/java/com/iluwatar/repository/AppConfig.java @@ -0,0 +1,157 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.repository; + +import java.sql.SQLException; +import java.util.List; +import java.util.Properties; + +import javax.sql.DataSource; + +import org.apache.commons.dbcp.BasicDataSource; +import org.hibernate.jpa.HibernatePersistenceProvider; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; + +/** + * This is the same example as in {@link App} but with annotations based + * configuration for Spring. + * + */ +@EnableJpaRepositories +public class AppConfig { + + /** + * Creation of H2 db + * + * @return A new Instance of DataSource + */ + @Bean(destroyMethod = "close") + public DataSource dataSource() { + BasicDataSource basicDataSource = new BasicDataSource(); + basicDataSource.setDriverClassName("org.h2.Driver"); + basicDataSource.setUrl("jdbc:h2:~/databases/person"); + basicDataSource.setUsername("sa"); + basicDataSource.setPassword("sa"); + return (DataSource) basicDataSource; + } + + /** + * Factory to create a especific instance of Entity Manager + */ + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactory() { + LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean(); + entityManager.setDataSource(dataSource()); + entityManager.setPackagesToScan("com.iluwatar"); + entityManager.setPersistenceProvider(new HibernatePersistenceProvider()); + entityManager.setJpaProperties(jpaProperties()); + + return entityManager; + } + + /** + * Properties for Jpa + */ + private static Properties jpaProperties() { + Properties properties = new Properties(); + properties.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect"); + properties.setProperty("hibernate.hbm2ddl.auto", "create-drop"); + return properties; + } + + /** + * Get transaction manager + */ + @Bean + public JpaTransactionManager transactionManager() throws SQLException { + JpaTransactionManager transactionManager = new JpaTransactionManager(); + transactionManager.setEntityManagerFactory(entityManagerFactory().getObject()); + return transactionManager; + } + + /** + * Program entry point + * + * @param args + * command line args + */ + public static void main(String[] args) { + + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + AppConfig.class); + PersonRepository repository = context.getBean(PersonRepository.class); + + Person peter = new Person("Peter", "Sagan", 17); + Person nasta = new Person("Nasta", "Kuzminova", 25); + Person john = new Person("John", "lawrence", 35); + Person terry = new Person("Terry", "Law", 36); + + // Add new Person records + repository.save(peter); + repository.save(nasta); + repository.save(john); + repository.save(terry); + + // Count Person records + System.out.println("Count Person records: " + repository.count()); + + // Print all records + List persons = (List) repository.findAll(); + for (Person person : persons) { + System.out.println(person); + } + + // Update Person + nasta.setName("Barbora"); + nasta.setSurname("Spotakova"); + repository.save(nasta); + + System.out.println("Find by id 2: " + repository.findOne(2L)); + + // Remove record from Person + repository.delete(2L); + + // count records + System.out.println("Count Person records: " + repository.count()); + + // find by name + Person p = repository.findOne(new PersonSpecifications.NameEqualSpec("John")); + System.out.println("Find by John is " + p); + + // find by age + persons = repository.findAll(new PersonSpecifications.AgeBetweenSpec(20, 40)); + + System.out.println("Find Person with age between 20,40: "); + for (Person person : persons) { + System.out.println(person); + } + + context.close(); + + } + +} diff --git a/repository/src/main/java/com/iluwatar/repository/Person.java b/repository/src/main/java/com/iluwatar/repository/Person.java new file mode 100644 index 000000000..b1559cf00 --- /dev/null +++ b/repository/src/main/java/com/iluwatar/repository/Person.java @@ -0,0 +1,145 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.repository; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +/** + * + * Person entity + * + */ +@Entity +public class Person { + + @Id + @GeneratedValue + private Long id; + private String name; + private String surname; + + private int age; + + public Person() { + } + + /** + * Constructor + */ + public Person(String name, String surname, int age) { + this.name = name; + this.surname = surname; + this.age = age; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSurname() { + return surname; + } + + public void setSurname(String surname) { + this.surname = surname; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + @Override + public String toString() { + return "Person [id=" + id + ", name=" + name + ", surname=" + surname + ", age=" + age + "]"; + } + + @Override + public int hashCode() { + + final int prime = 31; + int result = 1; + result = prime * result + age; + result = prime * result + (id == null ? 0 : id.hashCode()); + result = prime * result + (name == null ? 0 : name.hashCode()); + result = prime * result + (surname == null ? 0 : surname.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Person other = (Person) obj; + if (age != other.age) { + return false; + } + if (id == null) { + if (other.id != null) { + return false; + } + } else if (!id.equals(other.id)) { + return false; + } + if (name == null) { + if (other.name != null) { + return false; + } + } else if (!name.equals(other.name)) { + return false; + } + if (surname == null) { + if (other.surname != null) { + return false; + } + } else if (!surname.equals(other.surname)) { + return false; + } + return true; + } + +} diff --git a/repository/src/main/java/com/iluwatar/repository/PersonRepository.java b/repository/src/main/java/com/iluwatar/repository/PersonRepository.java new file mode 100644 index 000000000..eb9addd62 --- /dev/null +++ b/repository/src/main/java/com/iluwatar/repository/PersonRepository.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.repository; + +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +/** + * + * Person repository + * + */ +@Repository +public interface PersonRepository + extends CrudRepository, JpaSpecificationExecutor { + + Person findByName(String name); +} diff --git a/repository/src/main/java/com/iluwatar/repository/PersonSpecifications.java b/repository/src/main/java/com/iluwatar/repository/PersonSpecifications.java new file mode 100644 index 000000000..81394fda3 --- /dev/null +++ b/repository/src/main/java/com/iluwatar/repository/PersonSpecifications.java @@ -0,0 +1,79 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.repository; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; + +import org.springframework.data.jpa.domain.Specification; + +/** + * Helper class, includes vary Specification as the abstraction of sql query criteria + */ +public class PersonSpecifications { + + public static class AgeBetweenSpec implements Specification { + + private int from; + + private int to; + + public AgeBetweenSpec(int from, int to) { + this.from = from; + this.to = to; + } + + @Override + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + + return cb.between(root.get("age"), from, to); + + } + + } + + /** + * Name specification + * + */ + public static class NameEqualSpec implements Specification { + + public String name; + + public NameEqualSpec(String name) { + this.name = name; + } + + /** + * Get predicate + */ + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + + return cb.equal(root.get("name"), this.name); + + } + } + +} diff --git a/repository/src/main/resources/META-INF/persistence.xml b/repository/src/main/resources/META-INF/persistence.xml new file mode 100644 index 000000000..db4c7be13 --- /dev/null +++ b/repository/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,32 @@ + + + + + + + diff --git a/repository/src/main/resources/applicationContext.xml b/repository/src/main/resources/applicationContext.xml new file mode 100644 index 000000000..8b4b81bb7 --- /dev/null +++ b/repository/src/main/resources/applicationContext.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/repository/src/test/java/com/iluwatar/repository/AnnotationBasedRepositoryTest.java b/repository/src/test/java/com/iluwatar/repository/AnnotationBasedRepositoryTest.java new file mode 100644 index 000000000..ccd3fcc41 --- /dev/null +++ b/repository/src/test/java/com/iluwatar/repository/AnnotationBasedRepositoryTest.java @@ -0,0 +1,132 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.repository; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.List; + +import javax.annotation.Resource; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; + +import com.google.common.collect.Lists; + +/** + * Test case to test the functions of {@link PersonRepository}, beside the CRUD functions, the query + * by {@link org.springframework.data.jpa.domain.Specification} are also test. + * + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = { AppConfig.class }, loader = AnnotationConfigContextLoader.class) +public class AnnotationBasedRepositoryTest { + + @Resource + private PersonRepository repository; + + Person peter = new Person("Peter", "Sagan", 17); + Person nasta = new Person("Nasta", "Kuzminova", 25); + Person john = new Person("John", "lawrence", 35); + Person terry = new Person("Terry", "Law", 36); + + List persons = Arrays.asList(peter, nasta, john, terry); + + /** + * Prepare data for test + */ + @Before + public void setup() { + + repository.save(persons); + } + + @Test + public void testFindAll() { + + List actuals = Lists.newArrayList(repository.findAll()); + assertTrue(actuals.containsAll(persons) && persons.containsAll(actuals)); + } + + @Test + public void testSave() { + + Person terry = repository.findByName("Terry"); + terry.setSurname("Lee"); + terry.setAge(47); + repository.save(terry); + + terry = repository.findByName("Terry"); + assertEquals(terry.getSurname(), "Lee"); + assertEquals(47, terry.getAge()); + } + + @Test + public void testDelete() { + + Person terry = repository.findByName("Terry"); + repository.delete(terry); + + assertEquals(3, repository.count()); + assertNull(repository.findByName("Terry")); + } + + @Test + public void testCount() { + + assertEquals(4, repository.count()); + } + + @Test + public void testFindAllByAgeBetweenSpec() { + + List persons = repository.findAll(new PersonSpecifications.AgeBetweenSpec(20, 40)); + + assertEquals(3, persons.size()); + assertTrue(persons.stream().allMatch((item) -> { + return item.getAge() > 20 && item.getAge() < 40; + })); + } + + @Test + public void testFindOneByNameEqualSpec() { + + Person actual = repository.findOne(new PersonSpecifications.NameEqualSpec("Terry")); + assertEquals(terry, actual); + } + + @After + public void cleanup() { + + repository.deleteAll(); + } + +} diff --git a/repository/src/test/java/com/iluwatar/repository/AppConfigTest.java b/repository/src/test/java/com/iluwatar/repository/AppConfigTest.java new file mode 100644 index 000000000..28623bf45 --- /dev/null +++ b/repository/src/test/java/com/iluwatar/repository/AppConfigTest.java @@ -0,0 +1,76 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.repository; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.sql.ResultSet; +import java.sql.SQLException; + +import javax.sql.DataSource; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; +import org.springframework.transaction.annotation.Transactional; + +/** + * This case is Just for test the Annotation Based configuration + * + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = { AppConfig.class }, loader = AnnotationConfigContextLoader.class) +public class AppConfigTest { + + @Autowired + DataSource dataSource; + + /** + * Test for bean instance + */ + @Test + public void testDataSource() { + assertNotNull(dataSource); + } + + /** + * Test for correct query execution + */ + @Test + @Transactional + public void testQuery() throws SQLException { + ResultSet resultSet = dataSource.getConnection().createStatement().executeQuery("SELECT 1"); + String result = null; + String expected = "1"; + while (resultSet.next()) { + result = resultSet.getString(1); + + } + assertTrue(result.equals(expected)); + } + +} diff --git a/repository/src/test/java/com/iluwatar/repository/AppTest.java b/repository/src/test/java/com/iluwatar/repository/AppTest.java new file mode 100644 index 000000000..e1fc27bef --- /dev/null +++ b/repository/src/test/java/com/iluwatar/repository/AppTest.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.repository; + +import org.junit.Test; + +import java.io.IOException; + +/** + * Tests that Repository example runs without errors. + */ +public class AppTest { + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } +} diff --git a/repository/src/test/java/com/iluwatar/repository/RepositoryTest.java b/repository/src/test/java/com/iluwatar/repository/RepositoryTest.java new file mode 100644 index 000000000..63f9f7c6d --- /dev/null +++ b/repository/src/test/java/com/iluwatar/repository/RepositoryTest.java @@ -0,0 +1,130 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.repository; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.List; + +import javax.annotation.Resource; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.google.common.collect.Lists; + +/** + * Test case to test the functions of {@link PersonRepository}, beside the CRUD functions, the query + * by {@link org.springframework.data.jpa.domain.Specification} are also test. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = { "classpath:applicationContext.xml" }) +public class RepositoryTest { + + @Resource + private PersonRepository repository; + + Person peter = new Person("Peter", "Sagan", 17); + Person nasta = new Person("Nasta", "Kuzminova", 25); + Person john = new Person("John", "lawrence", 35); + Person terry = new Person("Terry", "Law", 36); + + List persons = Arrays.asList(peter, nasta, john, terry); + + /** + * Prepare data for test + */ + @Before + public void setup() { + + repository.save(persons); + } + + @Test + public void testFindAll() { + + List actuals = Lists.newArrayList(repository.findAll()); + assertTrue(actuals.containsAll(persons) && persons.containsAll(actuals)); + } + + @Test + public void testSave() { + + Person terry = repository.findByName("Terry"); + terry.setSurname("Lee"); + terry.setAge(47); + repository.save(terry); + + terry = repository.findByName("Terry"); + assertEquals(terry.getSurname(), "Lee"); + assertEquals(47, terry.getAge()); + } + + @Test + public void testDelete() { + + Person terry = repository.findByName("Terry"); + repository.delete(terry); + + assertEquals(3, repository.count()); + assertNull(repository.findByName("Terry")); + } + + @Test + public void testCount() { + + assertEquals(4, repository.count()); + } + + @Test + public void testFindAllByAgeBetweenSpec() { + + List persons = repository.findAll(new PersonSpecifications.AgeBetweenSpec(20, 40)); + + assertEquals(3, persons.size()); + assertTrue(persons.stream().allMatch((item) -> { + return item.getAge() > 20 && item.getAge() < 40; + })); + } + + @Test + public void testFindOneByNameEqualSpec() { + + Person actual = repository.findOne(new PersonSpecifications.NameEqualSpec("Terry")); + assertEquals(terry, actual); + } + + @After + public void cleanup() { + + repository.deleteAll(); + } + +} diff --git a/resource-acquisition-is-initialization/README.md b/resource-acquisition-is-initialization/README.md new file mode 100644 index 000000000..821f220d7 --- /dev/null +++ b/resource-acquisition-is-initialization/README.md @@ -0,0 +1,21 @@ +--- +layout: pattern +title: Resource Acquisition Is Initialization +folder: resource-acquisition-is-initialization +permalink: /patterns/resource-acquisition-is-initialization/ +categories: Other +tags: + - Java + - Difficulty-Beginner + - Idiom +--- + +## Intent +Resource Acquisition Is Initialization pattern can be used to implement exception safe resource management. + +![alt text](./etc/resource-acquisition-is-initialization.png "Resource Acquisition Is Initialization") + +## Applicability +Use the Resource Acquisition Is Initialization pattern when + +* you have resources that must be closed in every condition diff --git a/resource-acquisition-is-initialization/pom.xml b/resource-acquisition-is-initialization/pom.xml index 978598909..7b411f46c 100644 --- a/resource-acquisition-is-initialization/pom.xml +++ b/resource-acquisition-is-initialization/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT resource-acquisition-is-initialization @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/App.java b/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/App.java new file mode 100644 index 000000000..413fb0eab --- /dev/null +++ b/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/App.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.resource.acquisition.is.initialization; + +/** + * + * Resource Acquisition Is Initialization pattern was developed for exception safe resource + * management by C++ creator Bjarne Stroustrup. + *

+ * In RAII resource is tied to object lifetime: resource allocation is done during object creation + * while resource deallocation is done during object destruction. + *

+ * In Java RAII is achieved with try-with-resources statement and interfaces {@link Closeable} and + * {@link AutoCloseable}. The try-with-resources statement ensures that each resource is closed at + * the end of the statement. Any object that implements {@link java.lang.AutoCloseable}, which + * includes all objects which implement {@link java.io.Closeable}, can be used as a resource. + * + * In this example, {@link SlidingDoor} implements {@link AutoCloseable} and {@link TreasureChest} + * implements {@link Closeable}. Running the example, we can observe that both resources are + * automatically closed. + *

+ * http://docs.oracle.com/javase/7/docs/technotes/guides/language/try-with-resources.html + * + */ +public class App { + + /** + * Program entry point + */ + public static void main(String[] args) throws Exception { + + try (SlidingDoor slidingDoor = new SlidingDoor()) { + System.out.println("Walking in."); + } + + try (TreasureChest treasureChest = new TreasureChest()) { + System.out.println("Looting contents."); + } + } +} diff --git a/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/SlidingDoor.java b/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/SlidingDoor.java new file mode 100644 index 000000000..3a1a35f2d --- /dev/null +++ b/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/SlidingDoor.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.resource.acquisition.is.initialization; + +/** + * + * SlidingDoor resource + * + */ +public class SlidingDoor implements AutoCloseable { + + public SlidingDoor() { + System.out.println("Sliding door opens."); + } + + @Override + public void close() throws Exception { + System.out.println("Sliding door closes."); + } +} diff --git a/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/TreasureChest.java b/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/TreasureChest.java new file mode 100644 index 000000000..e7b7ebab6 --- /dev/null +++ b/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/TreasureChest.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.resource.acquisition.is.initialization; + +import java.io.Closeable; +import java.io.IOException; + +/** + * + * TreasureChest resource + * + */ +public class TreasureChest implements Closeable { + + public TreasureChest() { + System.out.println("Treasure chest opens."); + } + + @Override + public void close() throws IOException { + System.out.println("Treasure chest closes."); + } +} diff --git a/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resourceacquisitionisinitialization/App.java b/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resourceacquisitionisinitialization/App.java deleted file mode 100644 index ebf8a1dc7..000000000 --- a/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resourceacquisitionisinitialization/App.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.iluwatar.resourceacquisitionisinitialization; - -/** - * - * Resource Acquisition Is Initialization pattern was developed - * for exception safe resource management by C++ creator Bjarne - * Stroustrup. - * - * In RAII resource is tied to object lifetime: resource allocation - * is done during object creation while resource deallocation is - * done during object destruction. - * - * In Java RAII is achieved with try-with-resources statement and - * interfaces Closeable and AutoCloseable. The try-with-resources - * statement ensures that each resource is closed at the end of the - * statement. Any object that implements java.lang.AutoCloseable, which - * includes all objects which implement java.io.Closeable, can be used - * as a resource. - * - * In this example, SlidingDoor implements AutoCloseable and - * TreasureChest implements Closeable. Running the example, we can - * observe that both resources are automatically closed. - * - * http://docs.oracle.com/javase/7/docs/technotes/guides/language/try-with-resources.html - * - */ -public class App { - - public static void main( String[] args ) throws Exception { - - try (SlidingDoor slidingDoor = new SlidingDoor()) { - System.out.println("Walking in."); - } - - try (TreasureChest treasureChest = new TreasureChest()) { - System.out.println("Looting contents."); - } - } -} diff --git a/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resourceacquisitionisinitialization/SlidingDoor.java b/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resourceacquisitionisinitialization/SlidingDoor.java deleted file mode 100644 index c7181c8ba..000000000 --- a/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resourceacquisitionisinitialization/SlidingDoor.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.iluwatar.resourceacquisitionisinitialization; - -/** - * - * SlidingDoor resource - * - */ -public class SlidingDoor implements AutoCloseable { - - public SlidingDoor() { - System.out.println("Sliding door opens."); - } - - @Override - public void close() throws Exception { - System.out.println("Sliding door closes."); - } -} diff --git a/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resourceacquisitionisinitialization/TreasureChest.java b/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resourceacquisitionisinitialization/TreasureChest.java deleted file mode 100644 index c8f6b8ba4..000000000 --- a/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resourceacquisitionisinitialization/TreasureChest.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.iluwatar.resourceacquisitionisinitialization; - -import java.io.Closeable; -import java.io.IOException; - -/** - * - * TreasureChest resource - * - */ -public class TreasureChest implements Closeable { - - public TreasureChest() { - System.out.println("Treasure chest opens."); - } - - @Override - public void close() throws IOException { - System.out.println("Treasure chest closes."); - } -} diff --git a/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/AppTest.java b/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/AppTest.java new file mode 100644 index 000000000..c0e6a29b7 --- /dev/null +++ b/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/AppTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.resource.acquisition.is.initialization; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() throws Exception { + String[] args = {}; + App.main(args); + } +} diff --git a/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/ClosableTest.java b/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/ClosableTest.java new file mode 100644 index 000000000..55bdaf19c --- /dev/null +++ b/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/ClosableTest.java @@ -0,0 +1,49 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.resource.acquisition.is.initialization; + +import org.junit.Test; +import org.mockito.InOrder; + +import static org.mockito.Mockito.inOrder; + +/** + * Date: 12/28/15 - 9:31 PM + * + * @author Jeroen Meulemeester + */ +public class ClosableTest extends StdOutTest { + + @Test + public void testOpenClose() throws Exception { + final InOrder inOrder = inOrder(getStdOutMock()); + try (final SlidingDoor door = new SlidingDoor(); final TreasureChest chest = new TreasureChest()) { + inOrder.verify(getStdOutMock()).println("Sliding door opens."); + inOrder.verify(getStdOutMock()).println("Treasure chest opens."); + } + inOrder.verify(getStdOutMock()).println("Treasure chest closes."); + inOrder.verify(getStdOutMock()).println("Sliding door closes."); + inOrder.verifyNoMoreInteractions(); + } + +} \ No newline at end of file diff --git a/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/StdOutTest.java b/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/StdOutTest.java new file mode 100644 index 000000000..42cb42e6b --- /dev/null +++ b/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/StdOutTest.java @@ -0,0 +1,75 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.resource.acquisition.is.initialization; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since some actions don't have any + * influence on accessible objects, except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resourceacquisitionisinitialization/AppTest.java b/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resourceacquisitionisinitialization/AppTest.java deleted file mode 100644 index 5f71bbddf..000000000 --- a/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resourceacquisitionisinitialization/AppTest.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.iluwatar.resourceacquisitionisinitialization; - -import org.junit.Test; - -import com.iluwatar.resourceacquisitionisinitialization.App; - -public class AppTest { - - @Test - public void test() throws Exception { - String[] args = {}; - App.main(args); - } -} diff --git a/semaphore/README.md b/semaphore/README.md new file mode 100644 index 000000000..46ccd7b8e --- /dev/null +++ b/semaphore/README.md @@ -0,0 +1,33 @@ +--- +layout: pattern +title: Semaphore +folder: semaphore +permalink: /patterns/semaphore/ +categories: Concurrency +tags: + - Java + - Difficulty-Intermediate +--- + +## Also known as +Counting Semaphore + +## Intent +Create a lock which mediates access to a pool of resources. +Only a limited number of threads, specified at the creation +of the semaphore, can access the resources at any given time. +A semaphore which only allows one concurrent access to a resource +is called a binary semaphore. + +![alt text](./etc/semaphore.png "Semaphore") + +## Applicability +Use a Semaphore when + +* you have a pool of resources to allocate to different threads +* concurrent access to a resource could lead to a race condition + +## Credits + +* [Semaphore(programming)] (http://en.wikipedia.org/wiki/Semaphore_(programming)) +* [Semaphores] (http://tutorials.jenkov.com/java-concurrency/semaphores.html) diff --git a/semaphore/etc/semaphore.png b/semaphore/etc/semaphore.png new file mode 100644 index 000000000..a2ff6d0c4 Binary files /dev/null and b/semaphore/etc/semaphore.png differ diff --git a/semaphore/pom.xml b/semaphore/pom.xml new file mode 100644 index 000000000..1b3bf8b5d --- /dev/null +++ b/semaphore/pom.xml @@ -0,0 +1,42 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + semaphore + + + junit + junit + test + + + diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/App.java b/semaphore/src/main/java/com/iluwatar/semaphore/App.java new file mode 100644 index 000000000..272de37b0 --- /dev/null +++ b/semaphore/src/main/java/com/iluwatar/semaphore/App.java @@ -0,0 +1,50 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.semaphore; + +/** + * A Semaphore mediates access by a group of threads to a pool of resources. + *

+ * In this example a group of customers are taking fruit from a fruit shop. + * There is a bowl each of apples, oranges and lemons. Only one customer can + * access a bowl simultaneously. A Semaphore is used to indicate how many + * resources are currently available and must be acquired in order for a bowl + * to be given to a customer. Customers continually try to take fruit until + * there is no fruit left in the shop. + */ +public class App { + + /** + * main method + */ + public static void main(String[] args) { + FruitShop shop = new FruitShop(); + new Customer("Peter", shop).start(); + new Customer("Paul", shop).start(); + new Customer("Mary", shop).start(); + new Customer("John", shop).start(); + new Customer("Ringo", shop).start(); + new Customer("George", shop).start(); + } + +} diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/Customer.java b/semaphore/src/main/java/com/iluwatar/semaphore/Customer.java new file mode 100644 index 000000000..0a4713438 --- /dev/null +++ b/semaphore/src/main/java/com/iluwatar/semaphore/Customer.java @@ -0,0 +1,76 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.semaphore; + +/** + * A Customer attempts to repeatedly take Fruit from the FruitShop by + * taking Fruit from FruitBowl instances. + */ +public class Customer extends Thread { + + /** + * Name of the Customer. + */ + private final String name; + + /** + * The FruitShop he is using. + */ + private final FruitShop fruitShop; + + /** + * Their bowl of Fruit. + */ + private final FruitBowl fruitBowl; + + /** + * Customer constructor + */ + public Customer(String name, FruitShop fruitShop) { + this.name = name; + this.fruitShop = fruitShop; + this.fruitBowl = new FruitBowl(); + } + + /** + * The Customer repeatedly takes Fruit from the FruitShop until no Fruit + * remains. + */ + public void run() { + + while (fruitShop.countFruit() > 0) { + FruitBowl bowl = fruitShop.takeBowl(); + Fruit fruit; + + if (bowl != null && (fruit = bowl.take()) != null) { + System.out.println(name + " took an " + fruit); + fruitBowl.put(fruit); + fruitShop.returnBowl(bowl); + } + } + + System.out.println(name + " took " + fruitBowl); + + } + +} diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/Fruit.java b/semaphore/src/main/java/com/iluwatar/semaphore/Fruit.java new file mode 100644 index 000000000..0a4224d20 --- /dev/null +++ b/semaphore/src/main/java/com/iluwatar/semaphore/Fruit.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.semaphore; + +/** + * Fruit is a resource stored in a FruitBowl. + */ +public class Fruit { + + public static enum FruitType { + ORANGE, APPLE, LEMON + } + + private FruitType type; + + public Fruit(FruitType type) { + this.type = type; + } + + public FruitType getType() { + return type; + } + + /** + * toString method + */ + public String toString() { + switch (type) { + case ORANGE: + return "Orange"; + case APPLE: + return "Apple"; + case LEMON: + return "Lemon"; + default: + return ""; + } + } + +} diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/FruitBowl.java b/semaphore/src/main/java/com/iluwatar/semaphore/FruitBowl.java new file mode 100644 index 000000000..ac2b87ada --- /dev/null +++ b/semaphore/src/main/java/com/iluwatar/semaphore/FruitBowl.java @@ -0,0 +1,88 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.semaphore; + +import java.util.ArrayList; + +/** + * A FruitBowl contains Fruit. + */ +public class FruitBowl { + + private ArrayList fruit = new ArrayList<>(); + + /** + * + * @return The amount of Fruit left in the bowl. + */ + public int countFruit() { + return fruit.size(); + } + + /** + * Put an item of Fruit into the bowl. + * + * @param f fruit + */ + public void put(Fruit f) { + fruit.add(f); + } + + /** + * Take an item of Fruit out of the bowl. + * @return The Fruit taken out of the bowl, or null if empty. + */ + public Fruit take() { + if (fruit.isEmpty()) { + return null; + } else { + return fruit.remove(0); + } + } + + /** + * toString method + */ + public String toString() { + int apples = 0; + int oranges = 0; + int lemons = 0; + + for (Fruit f : fruit) { + switch (f.getType()) { + case APPLE: + apples++; + break; + case ORANGE: + oranges++; + break; + case LEMON: + lemons++; + break; + default: + } + } + + return apples + " Apples, " + oranges + " Oranges, and " + lemons + " Lemons"; + } +} diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/FruitShop.java b/semaphore/src/main/java/com/iluwatar/semaphore/FruitShop.java new file mode 100644 index 000000000..315b46986 --- /dev/null +++ b/semaphore/src/main/java/com/iluwatar/semaphore/FruitShop.java @@ -0,0 +1,120 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.semaphore; + +/** + * A FruitShop contains three FruitBowl instances and controls access to them. + */ +public class FruitShop { + + /** + * The FruitBowl instances stored in the class. + */ + private FruitBowl[] bowls = { + new FruitBowl(), + new FruitBowl(), + new FruitBowl() + }; + + /** + * Access flags for each of the FruitBowl instances. + */ + private boolean[] available = { + true, + true, + true + }; + + /** + * The Semaphore that controls access to the class resources. + */ + private Semaphore semaphore; + + /** + * FruitShop constructor + */ + public FruitShop() { + for (int i = 0; i < 100; i++) { + bowls[0].put(new Fruit(Fruit.FruitType.APPLE)); + bowls[1].put(new Fruit(Fruit.FruitType.ORANGE)); + bowls[2].put(new Fruit(Fruit.FruitType.LEMON)); + } + + semaphore = new Semaphore(3); + } + + /** + * + * @return The amount of Fruit left in the shop. + */ + public synchronized int countFruit() { + return bowls[0].countFruit() + bowls[1].countFruit() + bowls[2].countFruit(); + } + + /** + * Method called by Customer to get a FruitBowl from the shop. This method + * will try to acquire the Semaphore before returning the first available + * FruitBowl. + */ + public synchronized FruitBowl takeBowl() { + + FruitBowl bowl = null; + + try { + semaphore.acquire(); + + if (available[0]) { + bowl = bowls[0]; + available[0] = false; + } else if (available[1]) { + bowl = bowls[1]; + available[1] = false; + } else if (available[2]) { + bowl = bowls[2]; + available[2] = false; + } + + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + semaphore.release(); + } + return bowl; + } + + /** + * Method called by a Customer instance to return a FruitBowl to the shop. + * This method releases the Semaphore, making the FruitBowl available to + * another Customer. + */ + public synchronized void returnBowl(FruitBowl bowl) { + if (bowl == bowls[0]) { + available[0] = true; + } else if (bowl == bowls[1]) { + available[1] = true; + } else if (bowl == bowls[2]) { + available [2] = true; + } + } + +} diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/Lock.java b/semaphore/src/main/java/com/iluwatar/semaphore/Lock.java new file mode 100644 index 000000000..4b651685c --- /dev/null +++ b/semaphore/src/main/java/com/iluwatar/semaphore/Lock.java @@ -0,0 +1,34 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.semaphore; + +/** + * Lock is an interface for a lock which can be acquired and released. + */ +public interface Lock { + + void acquire() throws InterruptedException; + + void release(); + +} diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/Semaphore.java b/semaphore/src/main/java/com/iluwatar/semaphore/Semaphore.java new file mode 100644 index 000000000..2e4a54c7c --- /dev/null +++ b/semaphore/src/main/java/com/iluwatar/semaphore/Semaphore.java @@ -0,0 +1,77 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.semaphore; + +/** + * Semaphore is an implementation of a semaphore lock. + */ +public class Semaphore implements Lock { + + private final int licenses; + /** + * The number of concurrent resource accesses which are allowed. + */ + private int counter; + + public Semaphore(int licenses) { + this.licenses = licenses; + this.counter = licenses; + } + + /** + * Returns the number of licenses managed by the Semaphore + */ + public int getNumLicenses() { + return licenses; + } + + /** + * Returns the number of available licenses + */ + public int getAvailableLicenses() { + return counter; + } + + /** + * Method called by a thread to acquire the lock. If there are no resources + * available this will wait until the lock has been released to re-attempt + * the acquire. + */ + public synchronized void acquire() throws InterruptedException { + while (counter == 0) { + wait(); + } + counter = counter - 1; + } + + /** + * Method called by a thread to release the lock. + */ + public synchronized void release() { + if (counter < licenses) { + counter = counter + 1; + notify(); + } + } + +} diff --git a/semaphore/src/test/java/com/iluwatar/semaphore/AppTest.java b/semaphore/src/test/java/com/iluwatar/semaphore/AppTest.java new file mode 100644 index 000000000..4b286588d --- /dev/null +++ b/semaphore/src/test/java/com/iluwatar/semaphore/AppTest.java @@ -0,0 +1,34 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.semaphore; + +import org.junit.Test; +import java.io.IOException; + +public class AppTest{ + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } +} \ No newline at end of file diff --git a/semaphore/src/test/java/com/iluwatar/semaphore/FruitBowlTest.java b/semaphore/src/test/java/com/iluwatar/semaphore/FruitBowlTest.java new file mode 100644 index 000000000..be31f77ab --- /dev/null +++ b/semaphore/src/test/java/com/iluwatar/semaphore/FruitBowlTest.java @@ -0,0 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.semaphore; + +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * Test taking from and putting Fruit into a FruitBowl + */ +public class FruitBowlTest { + + @Test + public void fruitBowlTest() { + FruitBowl fbowl = new FruitBowl(); + + assertEquals(fbowl.countFruit(), 0); + + for (int i = 1; i <= 10; i++) { + fbowl.put(new Fruit(Fruit.FruitType.LEMON)); + assertEquals(fbowl.countFruit(), i); + } + + for (int i = 9; i >= 0; i--) { + assertNotNull(fbowl.take()); + assertEquals(fbowl.countFruit(), i); + } + + assertNull(fbowl.take()); + } +} diff --git a/semaphore/src/test/java/com/iluwatar/semaphore/SemaphoreTest.java b/semaphore/src/test/java/com/iluwatar/semaphore/SemaphoreTest.java new file mode 100644 index 000000000..14587a485 --- /dev/null +++ b/semaphore/src/test/java/com/iluwatar/semaphore/SemaphoreTest.java @@ -0,0 +1,56 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.semaphore; + +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * Test case for acquiring and releasing a Semaphore + */ +public class SemaphoreTest { + + @Test + public void acquireReleaseTest() { + Semaphore sphore = new Semaphore(3); + + assertEquals(sphore.getAvailableLicenses(), 3); + + for (int i = 2; i >= 0; i--) { + try { + sphore.acquire(); + assertEquals(sphore.getAvailableLicenses(), i); + } catch (InterruptedException e) { + fail(e.toString()); + } + } + + for (int i = 1; i <= 3; i++) { + sphore.release(); + assertEquals(sphore.getAvailableLicenses(), i); + } + + sphore.release(); + assertEquals(sphore.getAvailableLicenses(), 3); + } +} diff --git a/servant/README.md b/servant/README.md new file mode 100644 index 000000000..3e82ab2cf --- /dev/null +++ b/servant/README.md @@ -0,0 +1,25 @@ +--- +layout: pattern +title: Servant +folder: servant +permalink: /patterns/servant/ +categories: Structural +tags: + - Java + - Difficulty-Beginner +--- + +## Intent +Servant is used for providing some behavior to a group of classes. +Instead of defining that behavior in each class - or when we cannot factor out +this behavior in the common parent class - it is defined once in the Servant. + +![alt text](./etc/servant-pattern.png "Servant") + +## Applicability +Use the Servant pattern when + +* when we want some objects to perform a common action and don't want to define this action as a method in every class. + +## Credits +* [Let's Modify the Objects-First Approach into Design-Patterns-First](http://edu.pecinovsky.cz/papers/2006_ITiCSE_Design_Patterns_First.pdf) diff --git a/servant/pom.xml b/servant/pom.xml index 72c0a91c2..c235b005c 100644 --- a/servant/pom.xml +++ b/servant/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT servant @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/servant/src/etc/servant.xml b/servant/src/etc/servant.xml index 8da8a9e0b..7b91d0900 100644 --- a/servant/src/etc/servant.xml +++ b/servant/src/etc/servant.xml @@ -1,4 +1,28 @@ +

diff --git a/servant/src/main/java/com/iluwatar/servant/App.java b/servant/src/main/java/com/iluwatar/servant/App.java index 4951c7270..92829441d 100644 --- a/servant/src/main/java/com/iluwatar/servant/App.java +++ b/servant/src/main/java/com/iluwatar/servant/App.java @@ -1,51 +1,81 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servant; import java.util.ArrayList; /** - * Servant offers some functionality to a group of classes without defining that functionality in each of them. - * A Servant is a class whose instance provides methods that take care of a desired service, - * while objects for which the servant does something, are taken as parameters. + * Servant offers some functionality to a group of classes without defining that functionality in + * each of them. A Servant is a class whose instance provides methods that take care of a desired + * service, while objects for which the servant does something, are taken as parameters. + *

+ * In this example {@link Servant} is serving {@link King} and {@link Queen}. * */ public class App { - static Servant jenkins = new Servant("Jenkins"); - static Servant travis = new Servant("Travis"); - public static void main(String[] args) { - scenario(jenkins, 1); - scenario(travis, 0); + static Servant jenkins = new Servant("Jenkins"); + static Servant travis = new Servant("Travis"); + + /** + * Program entry point + */ + public static void main(String[] args) { + scenario(jenkins, 1); + scenario(travis, 0); + } + + /** + * Can add a List with enum Actions for variable scenarios + */ + public static void scenario(Servant servant, int compliment) { + King k = new King(); + Queen q = new Queen(); + + ArrayList guests = new ArrayList<>(); + guests.add(k); + guests.add(q); + + // feed + servant.feed(k); + servant.feed(q); + // serve drinks + servant.giveWine(k); + servant.giveWine(q); + // compliment + servant.giveCompliments(guests.get(compliment)); + + // outcome of the night + for (Royalty r : guests) { + r.changeMood(); } - /* - * Can add a List with enum Actions for variable scenarios - * */ - public static void scenario(Servant servant, int compliment) { - King k = new King(); - Queen q = new Queen(); - - ArrayList guests = new ArrayList<>(); - guests.add(k); - guests.add(q); - - //feed - servant.feed(k); - servant.feed(q); - //serve drinks - servant.giveWine(k); - servant.giveWine(q); - //compliment - servant.GiveCompliments(guests.get(compliment)); - - //outcome of the night - for (Royalty r : guests) - r.changeMood(); - - //check your luck - if (servant.checkIfYouWillBeHanged(guests)) - System.out.println(servant.name + " will live another day"); - else - System.out.println("Poor " + servant.name + ". His days are numbered"); + // check your luck + if (servant.checkIfYouWillBeHanged(guests)) { + System.out.println(servant.name + " will live another day"); + } else { + System.out.println("Poor " + servant.name + ". His days are numbered"); } + } } diff --git a/servant/src/main/java/com/iluwatar/servant/King.java b/servant/src/main/java/com/iluwatar/servant/King.java index 24aa1b870..bc3f8cdcf 100644 --- a/servant/src/main/java/com/iluwatar/servant/King.java +++ b/servant/src/main/java/com/iluwatar/servant/King.java @@ -1,33 +1,65 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servant; +/** + * + * King + * + */ public class King implements Royalty { - private boolean isDrunk; - private boolean isHungry = true; - private boolean isHappy; - private boolean complimentReceived; - @Override - public void getFed() { - isHungry = false; - } + private boolean isDrunk; + private boolean isHungry = true; + private boolean isHappy; + private boolean complimentReceived; - @Override - public void getDrink() { - isDrunk = true; - } + @Override + public void getFed() { + isHungry = false; + } - public void receiveCompliments() { - complimentReceived = true; - } + @Override + public void getDrink() { + isDrunk = true; + } - @Override - public void changeMood() { - if (!isHungry && isDrunk) isHappy = true; - if (complimentReceived) isHappy = false; - } + public void receiveCompliments() { + complimentReceived = true; + } - @Override - public boolean getMood() { - return isHappy; + @Override + public void changeMood() { + if (!isHungry && isDrunk) { + isHappy = true; } + if (complimentReceived) { + isHappy = false; + } + } + + @Override + public boolean getMood() { + return isHappy; + } } diff --git a/servant/src/main/java/com/iluwatar/servant/Queen.java b/servant/src/main/java/com/iluwatar/servant/Queen.java index c581132de..3b6203f3e 100644 --- a/servant/src/main/java/com/iluwatar/servant/Queen.java +++ b/servant/src/main/java/com/iluwatar/servant/Queen.java @@ -1,38 +1,68 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servant; +/** + * + * Queen + * + */ public class Queen implements Royalty { - private boolean isDrunk = true; - private boolean isHungry; - private boolean isHappy; - private boolean isFlirty = true; - private boolean complimentReceived; - @Override - public void getFed() { - isHungry = false; - } + private boolean isDrunk = true; + private boolean isHungry; + private boolean isHappy; + private boolean isFlirty = true; + private boolean complimentReceived; - @Override - public void getDrink() { - isDrunk = true; - } + @Override + public void getFed() { + isHungry = false; + } - public void receiveCompliments() { - complimentReceived = true; - } + @Override + public void getDrink() { + isDrunk = true; + } - @Override - public void changeMood() { - if (complimentReceived && isFlirty && isDrunk) isHappy = true; - } + public void receiveCompliments() { + complimentReceived = true; + } - @Override - public boolean getMood() { - return isHappy; + @Override + public void changeMood() { + if (complimentReceived && isFlirty && isDrunk && !isHungry) { + isHappy = true; } + } - public void setFlirtiness(boolean f) { - this.isFlirty = f; - } + @Override + public boolean getMood() { + return isHappy; + } + + public void setFlirtiness(boolean f) { + this.isFlirty = f; + } } diff --git a/servant/src/main/java/com/iluwatar/servant/Royalty.java b/servant/src/main/java/com/iluwatar/servant/Royalty.java index 02ebe3cce..b628383c8 100644 --- a/servant/src/main/java/com/iluwatar/servant/Royalty.java +++ b/servant/src/main/java/com/iluwatar/servant/Royalty.java @@ -1,14 +1,41 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servant; +/** + * + * Royalty + * + */ interface Royalty { - void getFed(); + void getFed(); - void getDrink(); + void getDrink(); - void changeMood(); + void changeMood(); - void receiveCompliments(); + void receiveCompliments(); - boolean getMood(); + boolean getMood(); } diff --git a/servant/src/main/java/com/iluwatar/servant/Servant.java b/servant/src/main/java/com/iluwatar/servant/Servant.java index b29e7fe20..56d65bde2 100644 --- a/servant/src/main/java/com/iluwatar/servant/Servant.java +++ b/servant/src/main/java/com/iluwatar/servant/Servant.java @@ -1,31 +1,68 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servant; -import java.util.ArrayList; +import java.util.List; +/** + * + * Servant + * + */ public class Servant { - public String name; - - public Servant(String name){ - this.name = name; - } - public void feed(Royalty r){ - r.getFed(); - } - - public void giveWine(Royalty r){ - r.getDrink(); - } - - public void GiveCompliments(Royalty r){ - r.receiveCompliments(); - } - - public boolean checkIfYouWillBeHanged(ArrayList tableGuests){ - boolean anotherDay = true; - for( Royalty r : tableGuests ) - if( !r.getMood() ) anotherDay = false; - - return anotherDay; - } + public String name; + + /** + * Constructor + */ + public Servant(String name) { + this.name = name; + } + + public void feed(Royalty r) { + r.getFed(); + } + + public void giveWine(Royalty r) { + r.getDrink(); + } + + public void giveCompliments(Royalty r) { + r.receiveCompliments(); + } + + /** + * Check if we will be hanged + */ + public boolean checkIfYouWillBeHanged(List tableGuests) { + boolean anotherDay = true; + for (Royalty r : tableGuests) { + if (!r.getMood()) { + anotherDay = false; + } + } + + return anotherDay; + } } diff --git a/servant/src/test/java/com/iluwatar/servant/AppTest.java b/servant/src/test/java/com/iluwatar/servant/AppTest.java index bf6655f2a..a9e66e783 100644 --- a/servant/src/test/java/com/iluwatar/servant/AppTest.java +++ b/servant/src/test/java/com/iluwatar/servant/AppTest.java @@ -1,14 +1,39 @@ -package com.iluwatar.servant; - -import org.junit.Test; - -import com.iluwatar.servant.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.servant; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/servant/src/test/java/com/iluwatar/servant/KingTest.java b/servant/src/test/java/com/iluwatar/servant/KingTest.java new file mode 100644 index 000000000..c56d12b24 --- /dev/null +++ b/servant/src/test/java/com/iluwatar/servant/KingTest.java @@ -0,0 +1,104 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.servant; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Date: 12/28/15 - 9:40 PM + * + * @author Jeroen Meulemeester + */ +public class KingTest { + + @Test + public void testHungrySoberUncomplimentedKing() { + final King king = new King(); + king.changeMood(); + assertFalse(king.getMood()); + } + + @Test + public void testFedSoberUncomplimentedKing() { + final King king = new King(); + king.getFed(); + king.changeMood(); + assertFalse(king.getMood()); + } + + @Test + public void testHungryDrunkUncomplimentedKing() { + final King king = new King(); + king.getDrink(); + king.changeMood(); + assertFalse(king.getMood()); + } + + @Test + public void testHungrySoberComplimentedKing() { + final King king = new King(); + king.receiveCompliments(); + king.changeMood(); + assertFalse(king.getMood()); + } + + @Test + public void testFedDrunkUncomplimentedKing() { + final King king = new King(); + king.getFed(); + king.getDrink(); + king.changeMood(); + assertTrue(king.getMood()); + } + + @Test + public void testFedSoberComplimentedKing() { + final King king = new King(); + king.getFed(); + king.receiveCompliments(); + king.changeMood(); + assertFalse(king.getMood()); + } + + @Test + public void testFedDrunkComplimentedKing() { + final King king = new King(); + king.getFed(); + king.getDrink(); + king.receiveCompliments(); + king.changeMood(); + assertFalse(king.getMood()); + } + + @Test + public void testHungryDrunkComplimentedKing() { + final King king = new King(); + king.getDrink(); + king.receiveCompliments(); + king.changeMood(); + assertFalse(king.getMood()); + } + +} \ No newline at end of file diff --git a/servant/src/test/java/com/iluwatar/servant/QueenTest.java b/servant/src/test/java/com/iluwatar/servant/QueenTest.java new file mode 100644 index 000000000..85b22fb42 --- /dev/null +++ b/servant/src/test/java/com/iluwatar/servant/QueenTest.java @@ -0,0 +1,68 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.servant; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Date: 12/28/15 - 9:52 PM + * + * @author Jeroen Meulemeester + */ +public class QueenTest { + + @Test + public void testNotFlirtyUncomplemented() throws Exception { + final Queen queen = new Queen(); + queen.setFlirtiness(false); + queen.changeMood(); + assertFalse(queen.getMood()); + } + + @Test + public void testNotFlirtyComplemented() throws Exception { + final Queen queen = new Queen(); + queen.setFlirtiness(false); + queen.receiveCompliments(); + queen.changeMood(); + assertFalse(queen.getMood()); + } + + @Test + public void testFlirtyUncomplemented() throws Exception { + final Queen queen = new Queen(); + queen.changeMood(); + assertFalse(queen.getMood()); + } + + @Test + public void testFlirtyComplemented() throws Exception { + final Queen queen = new Queen(); + queen.receiveCompliments(); + queen.changeMood(); + assertTrue(queen.getMood()); + } + +} \ No newline at end of file diff --git a/servant/src/test/java/com/iluwatar/servant/ServantTest.java b/servant/src/test/java/com/iluwatar/servant/ServantTest.java new file mode 100644 index 000000000..e4087d86d --- /dev/null +++ b/servant/src/test/java/com/iluwatar/servant/ServantTest.java @@ -0,0 +1,92 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.servant; + +import org.junit.Test; + +import java.util.ArrayList; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +/** + * Date: 12/28/15 - 10:02 PM + * + * @author Jeroen Meulemeester + */ +public class ServantTest { + + @Test + public void testFeed() throws Exception { + final Royalty royalty = mock(Royalty.class); + final Servant servant = new Servant("test"); + servant.feed(royalty); + verify(royalty).getFed(); + verifyNoMoreInteractions(royalty); + } + + @Test + public void testGiveWine() throws Exception { + final Royalty royalty = mock(Royalty.class); + final Servant servant = new Servant("test"); + servant.giveWine(royalty); + verify(royalty).getDrink(); + verifyNoMoreInteractions(royalty); + } + + @Test + public void testGiveCompliments() throws Exception { + final Royalty royalty = mock(Royalty.class); + final Servant servant = new Servant("test"); + servant.giveCompliments(royalty); + verify(royalty).receiveCompliments(); + verifyNoMoreInteractions(royalty); + } + + @Test + public void testCheckIfYouWillBeHanged() throws Exception { + final Royalty goodMoodRoyalty = mock(Royalty.class); + when(goodMoodRoyalty.getMood()).thenReturn(true); + + final Royalty badMoodRoyalty = mock(Royalty.class); + when(badMoodRoyalty.getMood()).thenReturn(true); + + final ArrayList goodCompany = new ArrayList<>(); + goodCompany.add(goodMoodRoyalty); + goodCompany.add(goodMoodRoyalty); + goodCompany.add(goodMoodRoyalty); + + final ArrayList badCompany = new ArrayList<>(); + goodCompany.add(goodMoodRoyalty); + goodCompany.add(goodMoodRoyalty); + goodCompany.add(badMoodRoyalty); + + assertTrue(new Servant("test").checkIfYouWillBeHanged(goodCompany)); + assertTrue(new Servant("test").checkIfYouWillBeHanged(badCompany)); + + } + +} \ No newline at end of file diff --git a/service-layer/README.md b/service-layer/README.md new file mode 100644 index 000000000..9b685d4e3 --- /dev/null +++ b/service-layer/README.md @@ -0,0 +1,31 @@ +--- +layout: pattern +title: Service Layer +folder: service-layer +permalink: /patterns/service-layer/ +categories: Architectural +tags: + - Java + - Difficulty-Intermediate +--- + +## Intent +Service Layer is an abstraction over domain logic. Typically +applications require multiple kinds of interfaces to the data they store and +logic they implement: data loaders, user interfaces, integration gateways, and +others. Despite their different purposes, these interfaces often need common +interactions with the application to access and manipulate its data and invoke +its business logic. The Service Layer fulfills this role. + +![alt text](./etc/service-layer.png "Service Layer") + +## Applicability +Use the Service Layer pattern when + +* you want to encapsulate domain logic under API +* you need to implement multiple interfaces with common logic and data + +## Credits + +* [Martin Fowler - Service Layer](http://martinfowler.com/eaaCatalog/serviceLayer.html) +* [Patterns of Enterprise Application Architecture](http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420) diff --git a/service-layer/bin/pom.xml b/service-layer/bin/pom.xml index 40aa93005..05ed6860d 100644 --- a/service-layer/bin/pom.xml +++ b/service-layer/bin/pom.xml @@ -1,4 +1,28 @@ + 4.0.0 diff --git a/service-layer/pom.xml b/service-layer/pom.xml index 45d3eacd6..23d788595 100644 --- a/service-layer/pom.xml +++ b/service-layer/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT service-layer @@ -22,5 +46,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/app/App.java b/service-layer/src/main/java/com/iluwatar/servicelayer/app/App.java index ea0419606..8282d800c 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/app/App.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/app/App.java @@ -1,12 +1,34 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servicelayer.app; import java.util.List; import com.iluwatar.servicelayer.magic.MagicService; import com.iluwatar.servicelayer.magic.MagicServiceImpl; -import com.iluwatar.servicelayer.servicelayer.spell.Spell; -import com.iluwatar.servicelayer.servicelayer.spell.SpellDao; -import com.iluwatar.servicelayer.servicelayer.spell.SpellDaoImpl; +import com.iluwatar.servicelayer.spell.Spell; +import com.iluwatar.servicelayer.spell.SpellDao; +import com.iluwatar.servicelayer.spell.SpellDaoImpl; import com.iluwatar.servicelayer.spellbook.Spellbook; import com.iluwatar.servicelayer.spellbook.SpellbookDao; import com.iluwatar.servicelayer.spellbook.SpellbookDaoImpl; @@ -16,157 +38,169 @@ import com.iluwatar.servicelayer.wizard.WizardDaoImpl; /** - * Service layer defines an application's boundary with a layer of services that establishes - * a set of available operations and coordinates the application's response in each operation. - * - * Enterprise applications typically require different kinds of interfaces to the data - * they store and the logic they implement: data loaders, user interfaces, integration gateways, - * and others. Despite their different purposes, these interfaces often need common interactions - * with the application to access and manipulate its data and invoke its business logic. The - * interactions may be complex, involving transactions across multiple resources and the - * coordination of several responses to an action. Encoding the logic of the interactions - * separately in each interface causes a lot of duplication. - * - * The example application demonstrates interactions between a client (App) and a service - * (MagicService). The service is implemented with 3-layer architecture (entity, dao, service). - * For persistence the example uses in-memory H2 database which is populated on each application - * startup. + * Service layer defines an application's boundary with a layer of services that establishes a set + * of available operations and coordinates the application's response in each operation. + *

+ * Enterprise applications typically require different kinds of interfaces to the data they store + * and the logic they implement: data loaders, user interfaces, integration gateways, and others. + * Despite their different purposes, these interfaces often need common interactions with the + * application to access and manipulate its data and invoke its business logic. The interactions may + * be complex, involving transactions across multiple resources and the coordination of several + * responses to an action. Encoding the logic of the interactions separately in each interface + * causes a lot of duplication. + *

+ * The example application demonstrates interactions between a client ({@link App}) and a service ( + * {@link MagicService}). The service is implemented with 3-layer architecture (entity, dao, + * service). For persistence the example uses in-memory H2 database which is populated on each + * application startup. * */ public class App { - - public static void main( String[] args ) { - // populate the in-memory database - initData(); - // query the data using the service - queryData(); + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + // populate the in-memory database + initData(); + // query the data using the service + queryData(); + } + + /** + * Initialize data + */ + public static void initData() { + // spells + Spell spell1 = new Spell("Ice dart"); + Spell spell2 = new Spell("Invisibility"); + Spell spell3 = new Spell("Stun bolt"); + Spell spell4 = new Spell("Confusion"); + Spell spell5 = new Spell("Darkness"); + Spell spell6 = new Spell("Fireball"); + Spell spell7 = new Spell("Enchant weapon"); + Spell spell8 = new Spell("Rock armour"); + Spell spell9 = new Spell("Light"); + Spell spell10 = new Spell("Bee swarm"); + Spell spell11 = new Spell("Haste"); + Spell spell12 = new Spell("Levitation"); + Spell spell13 = new Spell("Magic lock"); + Spell spell14 = new Spell("Summon hell bat"); + Spell spell15 = new Spell("Water walking"); + Spell spell16 = new Spell("Magic storm"); + Spell spell17 = new Spell("Entangle"); + SpellDao spellDao = new SpellDaoImpl(); + spellDao.persist(spell1); + spellDao.persist(spell2); + spellDao.persist(spell3); + spellDao.persist(spell4); + spellDao.persist(spell5); + spellDao.persist(spell6); + spellDao.persist(spell7); + spellDao.persist(spell8); + spellDao.persist(spell9); + spellDao.persist(spell10); + spellDao.persist(spell11); + spellDao.persist(spell12); + spellDao.persist(spell13); + spellDao.persist(spell14); + spellDao.persist(spell15); + spellDao.persist(spell16); + spellDao.persist(spell17); + + // spellbooks + SpellbookDao spellbookDao = new SpellbookDaoImpl(); + Spellbook spellbook1 = new Spellbook("Book of Orgymon"); + spellbookDao.persist(spellbook1); + spellbook1.addSpell(spell1); + spellbook1.addSpell(spell2); + spellbook1.addSpell(spell3); + spellbook1.addSpell(spell4); + spellbookDao.merge(spellbook1); + Spellbook spellbook2 = new Spellbook("Book of Aras"); + spellbookDao.persist(spellbook2); + spellbook2.addSpell(spell5); + spellbook2.addSpell(spell6); + spellbookDao.merge(spellbook2); + Spellbook spellbook3 = new Spellbook("Book of Kritior"); + spellbookDao.persist(spellbook3); + spellbook3.addSpell(spell7); + spellbook3.addSpell(spell8); + spellbook3.addSpell(spell9); + spellbookDao.merge(spellbook3); + Spellbook spellbook4 = new Spellbook("Book of Tamaex"); + spellbookDao.persist(spellbook4); + spellbook4.addSpell(spell10); + spellbook4.addSpell(spell11); + spellbook4.addSpell(spell12); + spellbookDao.merge(spellbook4); + Spellbook spellbook5 = new Spellbook("Book of Idores"); + spellbookDao.persist(spellbook5); + spellbook5.addSpell(spell13); + spellbookDao.merge(spellbook5); + Spellbook spellbook6 = new Spellbook("Book of Opaen"); + spellbookDao.persist(spellbook6); + spellbook6.addSpell(spell14); + spellbook6.addSpell(spell15); + spellbookDao.merge(spellbook6); + Spellbook spellbook7 = new Spellbook("Book of Kihione"); + spellbookDao.persist(spellbook7); + spellbook7.addSpell(spell16); + spellbook7.addSpell(spell17); + spellbookDao.merge(spellbook7); + + // wizards + WizardDao wizardDao = new WizardDaoImpl(); + Wizard wizard1 = new Wizard("Aderlard Boud"); + wizardDao.persist(wizard1); + wizard1.addSpellbook(spellbookDao.findByName("Book of Orgymon")); + wizard1.addSpellbook(spellbookDao.findByName("Book of Aras")); + wizardDao.merge(wizard1); + Wizard wizard2 = new Wizard("Anaxis Bajraktari"); + wizardDao.persist(wizard2); + wizard2.addSpellbook(spellbookDao.findByName("Book of Kritior")); + wizard2.addSpellbook(spellbookDao.findByName("Book of Tamaex")); + wizardDao.merge(wizard2); + Wizard wizard3 = new Wizard("Xuban Munoa"); + wizardDao.persist(wizard3); + wizard3.addSpellbook(spellbookDao.findByName("Book of Idores")); + wizard3.addSpellbook(spellbookDao.findByName("Book of Opaen")); + wizardDao.merge(wizard3); + Wizard wizard4 = new Wizard("Blasius Dehooge"); + wizardDao.persist(wizard4); + wizard4.addSpellbook(spellbookDao.findByName("Book of Kihione")); + wizardDao.merge(wizard4); + } + + /** + * Query the data + */ + public static void queryData() { + MagicService service = + new MagicServiceImpl(new WizardDaoImpl(), new SpellbookDaoImpl(), new SpellDaoImpl()); + System.out.println("Enumerating all wizards"); + for (Wizard w : service.findAllWizards()) { + System.out.println(w.getName()); } - - public static void initData() { - // spells - Spell spell1 = new Spell("Ice dart"); - Spell spell2 = new Spell("Invisibility"); - Spell spell3 = new Spell("Stun bolt"); - Spell spell4 = new Spell("Confusion"); - Spell spell5 = new Spell("Darkness"); - Spell spell6 = new Spell("Fireball"); - Spell spell7 = new Spell("Enchant weapon"); - Spell spell8 = new Spell("Rock armour"); - Spell spell9 = new Spell("Light"); - Spell spell10 = new Spell("Bee swarm"); - Spell spell11 = new Spell("Haste"); - Spell spell12 = new Spell("Levitation"); - Spell spell13 = new Spell("Magic lock"); - Spell spell14 = new Spell("Summon hell bat"); - Spell spell15 = new Spell("Water walking"); - Spell spell16 = new Spell("Magic storm"); - Spell spell17 = new Spell("Entangle"); - SpellDao spellDao = new SpellDaoImpl(); - spellDao.persist(spell1); - spellDao.persist(spell2); - spellDao.persist(spell3); - spellDao.persist(spell4); - spellDao.persist(spell5); - spellDao.persist(spell6); - spellDao.persist(spell7); - spellDao.persist(spell8); - spellDao.persist(spell9); - spellDao.persist(spell10); - spellDao.persist(spell11); - spellDao.persist(spell12); - spellDao.persist(spell13); - spellDao.persist(spell14); - spellDao.persist(spell15); - spellDao.persist(spell16); - spellDao.persist(spell17); - - // spellbooks - SpellbookDao spellbookDao = new SpellbookDaoImpl(); - Spellbook spellbook1 = new Spellbook("Book of Orgymon"); - spellbookDao.persist(spellbook1); - spellbook1.addSpell(spell1); - spellbook1.addSpell(spell2); - spellbook1.addSpell(spell3); - spellbook1.addSpell(spell4); - spellbookDao.merge(spellbook1); - Spellbook spellbook2 = new Spellbook("Book of Aras"); - spellbookDao.persist(spellbook2); - spellbook2.addSpell(spell5); - spellbook2.addSpell(spell6); - spellbookDao.merge(spellbook2); - Spellbook spellbook3 = new Spellbook("Book of Kritior"); - spellbookDao.persist(spellbook3); - spellbook3.addSpell(spell7); - spellbook3.addSpell(spell8); - spellbook3.addSpell(spell9); - spellbookDao.merge(spellbook3); - Spellbook spellbook4 = new Spellbook("Book of Tamaex"); - spellbookDao.persist(spellbook4); - spellbook4.addSpell(spell10); - spellbook4.addSpell(spell11); - spellbook4.addSpell(spell12); - spellbookDao.merge(spellbook4); - Spellbook spellbook5 = new Spellbook("Book of Idores"); - spellbookDao.persist(spellbook5); - spellbook5.addSpell(spell13); - spellbookDao.merge(spellbook5); - Spellbook spellbook6 = new Spellbook("Book of Opaen"); - spellbookDao.persist(spellbook6); - spellbook6.addSpell(spell14); - spellbook6.addSpell(spell15); - spellbookDao.merge(spellbook6); - Spellbook spellbook7 = new Spellbook("Book of Kihione"); - spellbookDao.persist(spellbook7); - spellbook7.addSpell(spell16); - spellbook7.addSpell(spell17); - spellbookDao.merge(spellbook7); - - // wizards - WizardDao wizardDao = new WizardDaoImpl(); - Wizard wizard1 = new Wizard("Aderlard Boud"); - wizardDao.persist(wizard1); - wizard1.addSpellbook(spellbookDao.findByName("Book of Orgymon")); - wizard1.addSpellbook(spellbookDao.findByName("Book of Aras")); - wizardDao.merge(wizard1); - Wizard wizard2 = new Wizard("Anaxis Bajraktari"); - wizardDao.persist(wizard2); - wizard2.addSpellbook(spellbookDao.findByName("Book of Kritior")); - wizard2.addSpellbook(spellbookDao.findByName("Book of Tamaex")); - wizardDao.merge(wizard2); - Wizard wizard3 = new Wizard("Xuban Munoa"); - wizardDao.persist(wizard3); - wizard3.addSpellbook(spellbookDao.findByName("Book of Idores")); - wizard3.addSpellbook(spellbookDao.findByName("Book of Opaen")); - wizardDao.merge(wizard3); - Wizard wizard4 = new Wizard("Blasius Dehooge"); - wizardDao.persist(wizard4); - wizard4.addSpellbook(spellbookDao.findByName("Book of Kihione")); - wizardDao.merge(wizard4); + System.out.println("Enumerating all spellbooks"); + for (Spellbook s : service.findAllSpellbooks()) { + System.out.println(s.getName()); } - - public static void queryData() { - MagicService service = new MagicServiceImpl(new WizardDaoImpl(), new SpellbookDaoImpl(), new SpellDaoImpl()); - System.out.println("Enumerating all wizards"); - for (Wizard w: service.findAllWizards()) { - System.out.println(w.getName()); - } - System.out.println("Enumerating all spellbooks"); - for (Spellbook s: service.findAllSpellbooks()) { - System.out.println(s.getName()); - } - System.out.println("Enumerating all spells"); - for (Spell s: service.findAllSpells()) { - System.out.println(s.getName()); - } - System.out.println("Find wizards with spellbook 'Book of Idores'"); - List wizardsWithSpellbook = service.findWizardsWithSpellbook("Book of Idores"); - for (Wizard w: wizardsWithSpellbook) { - System.out.println(String.format("%s has 'Book of Idores'", w.getName())); - } - System.out.println("Find wizards with spell 'Fireball'"); - List wizardsWithSpell = service.findWizardsWithSpell("Fireball"); - for (Wizard w: wizardsWithSpell) { - System.out.println(String.format("%s has 'Fireball'", w.getName())); - } + System.out.println("Enumerating all spells"); + for (Spell s : service.findAllSpells()) { + System.out.println(s.getName()); } + System.out.println("Find wizards with spellbook 'Book of Idores'"); + List wizardsWithSpellbook = service.findWizardsWithSpellbook("Book of Idores"); + for (Wizard w : wizardsWithSpellbook) { + System.out.println(String.format("%s has 'Book of Idores'", w.getName())); + } + System.out.println("Find wizards with spell 'Fireball'"); + List wizardsWithSpell = service.findWizardsWithSpell("Fireball"); + for (Wizard w : wizardsWithSpell) { + System.out.println(String.format("%s has 'Fireball'", w.getName())); + } + } } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/common/BaseEntity.java b/service-layer/src/main/java/com/iluwatar/servicelayer/common/BaseEntity.java index ec0f8e197..53f5f7b0f 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/common/BaseEntity.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/common/BaseEntity.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servicelayer.common; import javax.persistence.Inheritance; @@ -11,9 +33,38 @@ import javax.persistence.Version; * */ @MappedSuperclass -@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS) -public class BaseEntity { - - @Version - private Long version; +@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) +public abstract class BaseEntity { + + @Version + private Long version; + + /** + * Indicates the unique id of this entity + * + * @return The id of the entity, or 'null' when not persisted + */ + public abstract Long getId(); + + /** + * Set the id of this entity + * + * @param id The new id + */ + public abstract void setId(Long id); + + /** + * Get the name of this entity + * + * @return The name of the entity + */ + public abstract String getName(); + + /** + * Set the name of this entity + * + * @param name The new name + */ + public abstract void setName(final String name); + } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/common/Dao.java b/service-layer/src/main/java/com/iluwatar/servicelayer/common/Dao.java index e1851b16f..1fed0864b 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/common/Dao.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/common/Dao.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servicelayer.common; import java.util.List; @@ -11,13 +33,13 @@ import java.util.List; */ public interface Dao { - E find(Long id); - - void persist(E entity); - - E merge(E entity); - - void delete(E entity); - - List findAll(); + E find(Long id); + + void persist(E entity); + + E merge(E entity); + + void delete(E entity); + + List findAll(); } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/common/DaoBaseImpl.java b/service-layer/src/main/java/com/iluwatar/servicelayer/common/DaoBaseImpl.java index bc0116dac..88ac2597d 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/common/DaoBaseImpl.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/common/DaoBaseImpl.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servicelayer.common; import java.lang.reflect.ParameterizedType; @@ -18,110 +40,110 @@ import com.iluwatar.servicelayer.hibernate.HibernateUtil; * */ public abstract class DaoBaseImpl implements Dao { - - @SuppressWarnings("unchecked") - protected Class persistentClass = (Class) ((ParameterizedType) getClass() - .getGenericSuperclass()).getActualTypeArguments()[0]; - protected Session getSession() { - return HibernateUtil.getSessionFactory().openSession(); - } - - @Override - public E find(Long id) { - Session session = getSession(); - Transaction tx = null; - E result = null; - try { - tx = session.beginTransaction(); - Criteria criteria = session.createCriteria(persistentClass); - criteria.add(Restrictions.idEq(id)); - result = (E) criteria.uniqueResult(); - tx.commit(); - } - catch (Exception e) { - if (tx!=null) tx.rollback(); - throw e; - } - finally { - session.close(); - } - return result; - } - - @Override - public void persist(E entity) { - Session session = getSession(); - Transaction tx = null; - try { - tx = session.beginTransaction(); - session.persist(entity); - tx.commit(); - } - catch (Exception e) { - if (tx!=null) tx.rollback(); - throw e; - } - finally { - session.close(); - } - } - - @Override - public E merge(E entity) { - Session session = getSession(); - Transaction tx = null; - E result = null; - try { - tx = session.beginTransaction(); - result = (E) session.merge(entity); - tx.commit(); - } - catch (Exception e) { - if (tx!=null) tx.rollback(); - throw e; - } - finally { - session.close(); - } - return result; - } - - @Override - public void delete(E entity) { - Session session = getSession(); - Transaction tx = null; - try { - tx = session.beginTransaction(); - session.delete(entity); - tx.commit(); - } - catch (Exception e) { - if (tx!=null) tx.rollback(); - throw e; - } - finally { - session.close(); - } - } - - @Override - public List findAll() { - Session session = getSession(); - Transaction tx = null; - List result = null; - try { - tx = session.beginTransaction(); - Criteria criteria = session.createCriteria(persistentClass); - result = criteria.list(); - } - catch (Exception e) { - if (tx!=null) tx.rollback(); - throw e; - } - finally { - session.close(); - } - return result; - } + @SuppressWarnings("unchecked") + protected Class persistentClass = (Class) ((ParameterizedType) getClass() + .getGenericSuperclass()).getActualTypeArguments()[0]; + + protected Session getSession() { + return HibernateUtil.getSessionFactory().openSession(); + } + + @Override + public E find(Long id) { + Session session = getSession(); + Transaction tx = null; + E result = null; + try { + tx = session.beginTransaction(); + Criteria criteria = session.createCriteria(persistentClass); + criteria.add(Restrictions.idEq(id)); + result = (E) criteria.uniqueResult(); + tx.commit(); + } catch (Exception e) { + if (tx != null) { + tx.rollback(); + } + throw e; + } finally { + session.close(); + } + return result; + } + + @Override + public void persist(E entity) { + Session session = getSession(); + Transaction tx = null; + try { + tx = session.beginTransaction(); + session.persist(entity); + tx.commit(); + } catch (Exception e) { + if (tx != null) { + tx.rollback(); + } + throw e; + } finally { + session.close(); + } + } + + @Override + public E merge(E entity) { + Session session = getSession(); + Transaction tx = null; + E result = null; + try { + tx = session.beginTransaction(); + result = (E) session.merge(entity); + tx.commit(); + } catch (Exception e) { + if (tx != null) { + tx.rollback(); + } + throw e; + } finally { + session.close(); + } + return result; + } + + @Override + public void delete(E entity) { + Session session = getSession(); + Transaction tx = null; + try { + tx = session.beginTransaction(); + session.delete(entity); + tx.commit(); + } catch (Exception e) { + if (tx != null) { + tx.rollback(); + } + throw e; + } finally { + session.close(); + } + } + + @Override + public List findAll() { + Session session = getSession(); + Transaction tx = null; + List result = null; + try { + tx = session.beginTransaction(); + Criteria criteria = session.createCriteria(persistentClass); + result = criteria.list(); + } catch (Exception e) { + if (tx != null) { + tx.rollback(); + } + throw e; + } finally { + session.close(); + } + return result; + } } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/hibernate/HibernateUtil.java b/service-layer/src/main/java/com/iluwatar/servicelayer/hibernate/HibernateUtil.java index 922fe9f21..b30b97b65 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/hibernate/HibernateUtil.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/hibernate/HibernateUtil.java @@ -1,40 +1,78 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servicelayer.hibernate; +import com.iluwatar.servicelayer.spell.Spell; +import com.iluwatar.servicelayer.spellbook.Spellbook; +import com.iluwatar.servicelayer.wizard.Wizard; + import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; -import com.iluwatar.servicelayer.servicelayer.spell.Spell; -import com.iluwatar.servicelayer.spellbook.Spellbook; -import com.iluwatar.servicelayer.wizard.Wizard; - /** - * - * Produces the Hibernate SessionFactory. - * + * Produces the Hibernate {@link SessionFactory}. */ -public class HibernateUtil { +public final class HibernateUtil { - private static final SessionFactory sessionFactory; + /** + * The cached session factory + */ + private static volatile SessionFactory sessionFactory; - static { - try { - sessionFactory = new Configuration() - .addAnnotatedClass(Wizard.class) - .addAnnotatedClass(Spellbook.class) - .addAnnotatedClass(Spell.class) - .setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect") - .setProperty("hibernate.connection.url", "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1") - .setProperty("hibernate.current_session_context_class", "thread") - .setProperty("hibernate.show_sql", "true") - .setProperty("hibernate.hbm2ddl.auto", "create-drop") - .buildSessionFactory(); - } catch (Throwable ex) { - System.err.println("Initial SessionFactory creation failed." + ex); - throw new ExceptionInInitializerError(ex); - } - } + private HibernateUtil() { + } + + /** + * Create the current session factory instance, create a new one when there is none yet. + * + * @return The session factory + */ + public static synchronized SessionFactory getSessionFactory() { + if (sessionFactory == null) { + try { + sessionFactory = + new Configuration().addAnnotatedClass(Wizard.class).addAnnotatedClass(Spellbook.class) + .addAnnotatedClass(Spell.class) + .setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect") + .setProperty("hibernate.connection.url", "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1") + .setProperty("hibernate.current_session_context_class", "thread") + .setProperty("hibernate.show_sql", "true") + .setProperty("hibernate.hbm2ddl.auto", "create-drop").buildSessionFactory(); + } catch (Throwable ex) { + System.err.println("Initial SessionFactory creation failed." + ex); + throw new ExceptionInInitializerError(ex); + } + } + return sessionFactory; + } + + /** + * Drop the current connection, resulting in a create-drop clean database next time. This is + * mainly used for JUnit testing since one test should not influence the other + */ + public static void dropSession() { + getSessionFactory().close(); + sessionFactory = null; + } - public static SessionFactory getSessionFactory() { - return sessionFactory; - } } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/magic/MagicService.java b/service-layer/src/main/java/com/iluwatar/servicelayer/magic/MagicService.java index a6046742b..c88e1f538 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/magic/MagicService.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/magic/MagicService.java @@ -1,8 +1,30 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servicelayer.magic; import java.util.List; -import com.iluwatar.servicelayer.servicelayer.spell.Spell; +import com.iluwatar.servicelayer.spell.Spell; import com.iluwatar.servicelayer.spellbook.Spellbook; import com.iluwatar.servicelayer.wizard.Wizard; @@ -14,13 +36,13 @@ import com.iluwatar.servicelayer.wizard.Wizard; */ public interface MagicService { - List findAllWizards(); + List findAllWizards(); - List findAllSpellbooks(); - - List findAllSpells(); + List findAllSpellbooks(); - List findWizardsWithSpellbook(String name); + List findAllSpells(); - List findWizardsWithSpell(String name); + List findWizardsWithSpellbook(String name); + + List findWizardsWithSpell(String name); } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/magic/MagicServiceImpl.java b/service-layer/src/main/java/com/iluwatar/servicelayer/magic/MagicServiceImpl.java index e5599fc4e..71756d4cf 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/magic/MagicServiceImpl.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/magic/MagicServiceImpl.java @@ -1,10 +1,32 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servicelayer.magic; import java.util.ArrayList; import java.util.List; -import com.iluwatar.servicelayer.servicelayer.spell.Spell; -import com.iluwatar.servicelayer.servicelayer.spell.SpellDao; +import com.iluwatar.servicelayer.spell.Spell; +import com.iluwatar.servicelayer.spell.SpellDao; import com.iluwatar.servicelayer.spellbook.Spellbook; import com.iluwatar.servicelayer.spellbook.SpellbookDao; import com.iluwatar.servicelayer.wizard.Wizard; @@ -16,42 +38,45 @@ import com.iluwatar.servicelayer.wizard.WizardDao; * */ public class MagicServiceImpl implements MagicService { - - private WizardDao wizardDao; - private SpellbookDao spellbookDao; - private SpellDao spellDao; - public MagicServiceImpl(WizardDao wizardDao, SpellbookDao spellbookDao, SpellDao spellDao) { - this.wizardDao = wizardDao; - this.spellbookDao = spellbookDao; - this.spellDao = spellDao; - } + private WizardDao wizardDao; + private SpellbookDao spellbookDao; + private SpellDao spellDao; - @Override - public List findAllWizards() { - return wizardDao.findAll(); - } + /** + * Constructor + */ + public MagicServiceImpl(WizardDao wizardDao, SpellbookDao spellbookDao, SpellDao spellDao) { + this.wizardDao = wizardDao; + this.spellbookDao = spellbookDao; + this.spellDao = spellDao; + } - @Override - public List findAllSpellbooks() { - return spellbookDao.findAll(); - } + @Override + public List findAllWizards() { + return wizardDao.findAll(); + } - @Override - public List findAllSpells() { - return spellDao.findAll(); - } + @Override + public List findAllSpellbooks() { + return spellbookDao.findAll(); + } - @Override - public List findWizardsWithSpellbook(String name) { - Spellbook spellbook = spellbookDao.findByName(name); - return new ArrayList(spellbook.getWizards()); - } + @Override + public List findAllSpells() { + return spellDao.findAll(); + } - @Override - public List findWizardsWithSpell(String name) { - Spell spell = spellDao.findByName(name); - Spellbook spellbook = spell.getSpellbook(); - return new ArrayList(spellbook.getWizards()); - } + @Override + public List findWizardsWithSpellbook(String name) { + Spellbook spellbook = spellbookDao.findByName(name); + return new ArrayList<>(spellbook.getWizards()); + } + + @Override + public List findWizardsWithSpell(String name) { + Spell spell = spellDao.findByName(name); + Spellbook spellbook = spell.getSpellbook(); + return new ArrayList<>(spellbook.getWizards()); + } } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/servicelayer/spell/Spell.java b/service-layer/src/main/java/com/iluwatar/servicelayer/servicelayer/spell/Spell.java deleted file mode 100644 index 5dc7a6acc..000000000 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/servicelayer/spell/Spell.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.iluwatar.servicelayer.servicelayer.spell; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.Table; - -import com.iluwatar.servicelayer.common.BaseEntity; -import com.iluwatar.servicelayer.spellbook.Spellbook; - -/** - * - * Spell entity. - * - */ -@Entity -@Table(name="SPELL") -public class Spell extends BaseEntity { - - private String name; - - public Spell() { - } - - public Spell(String name) { - this(); - this.name = name; - } - - @Id - @GeneratedValue - @Column(name = "SPELL_ID") - private Long id; - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - @ManyToOne - @JoinColumn(name="SPELLBOOK_ID_FK", referencedColumnName="SPELLBOOK_ID") - private Spellbook spellbook; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Spellbook getSpellbook() { - return spellbook; - } - - public void setSpellbook(Spellbook spellbook) { - this.spellbook = spellbook; - } - - @Override - public String toString() { - return name; - } -} diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/servicelayer/spell/SpellDao.java b/service-layer/src/main/java/com/iluwatar/servicelayer/servicelayer/spell/SpellDao.java deleted file mode 100644 index 079ef39a0..000000000 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/servicelayer/spell/SpellDao.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.iluwatar.servicelayer.servicelayer.spell; - -import com.iluwatar.servicelayer.common.Dao; - -/** - * - * SpellDao interface. - * - */ -public interface SpellDao extends Dao { - - Spell findByName(String name); - -} diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/servicelayer/spell/SpellDaoImpl.java b/service-layer/src/main/java/com/iluwatar/servicelayer/servicelayer/spell/SpellDaoImpl.java deleted file mode 100644 index d8655ac3a..000000000 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/servicelayer/spell/SpellDaoImpl.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.iluwatar.servicelayer.servicelayer.spell; - -import org.hibernate.Criteria; -import org.hibernate.Session; -import org.hibernate.Transaction; -import org.hibernate.criterion.Restrictions; - -import com.iluwatar.servicelayer.common.DaoBaseImpl; - -/** - * - * SpellDao implementation. - * - */ -public class SpellDaoImpl extends DaoBaseImpl implements SpellDao { - - @Override - public Spell findByName(String name) { - Session session = getSession(); - Transaction tx = null; - Spell result = null; - try { - tx = session.beginTransaction(); - Criteria criteria = session.createCriteria(persistentClass); - criteria.add(Restrictions.eq("name", name)); - result = (Spell) criteria.uniqueResult(); - result.getSpellbook().getWizards().size(); - tx.commit(); - } - catch (Exception e) { - if (tx!=null) tx.rollback(); - throw e; - } - finally { - session.close(); - } - return result; - } -} diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/spell/Spell.java b/service-layer/src/main/java/com/iluwatar/servicelayer/spell/Spell.java new file mode 100644 index 000000000..caf1b2c91 --- /dev/null +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/spell/Spell.java @@ -0,0 +1,91 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.servicelayer.spell; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import com.iluwatar.servicelayer.common.BaseEntity; +import com.iluwatar.servicelayer.spellbook.Spellbook; + +/** + * + * Spell entity. + * + */ +@Entity +@Table(name = "SPELL") +public class Spell extends BaseEntity { + + private String name; + + @Id + @GeneratedValue + @Column(name = "SPELL_ID") + private Long id; + + @ManyToOne + @JoinColumn(name = "SPELLBOOK_ID_FK", referencedColumnName = "SPELLBOOK_ID") + private Spellbook spellbook; + + public Spell() {} + + public Spell(String name) { + this(); + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Spellbook getSpellbook() { + return spellbook; + } + + public void setSpellbook(Spellbook spellbook) { + this.spellbook = spellbook; + } + + @Override + public String toString() { + return name; + } +} diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/spell/SpellDao.java b/service-layer/src/main/java/com/iluwatar/servicelayer/spell/SpellDao.java new file mode 100644 index 000000000..10a35b73a --- /dev/null +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/spell/SpellDao.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.servicelayer.spell; + +import com.iluwatar.servicelayer.common.Dao; + +/** + * + * SpellDao interface. + * + */ +public interface SpellDao extends Dao { + + Spell findByName(String name); + +} diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/spell/SpellDaoImpl.java b/service-layer/src/main/java/com/iluwatar/servicelayer/spell/SpellDaoImpl.java new file mode 100644 index 000000000..bd1860a6b --- /dev/null +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/spell/SpellDaoImpl.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.servicelayer.spell; + +import com.iluwatar.servicelayer.common.DaoBaseImpl; + +import org.hibernate.Criteria; +import org.hibernate.Session; +import org.hibernate.Transaction; +import org.hibernate.criterion.Restrictions; + +/** + * + * SpellDao implementation. + * + */ +public class SpellDaoImpl extends DaoBaseImpl implements SpellDao { + + @Override + public Spell findByName(String name) { + Session session = getSession(); + Transaction tx = null; + Spell result = null; + try { + tx = session.beginTransaction(); + Criteria criteria = session.createCriteria(persistentClass); + criteria.add(Restrictions.eq("name", name)); + result = (Spell) criteria.uniqueResult(); + tx.commit(); + } catch (Exception e) { + if (tx != null) { + tx.rollback(); + } + throw e; + } finally { + session.close(); + } + return result; + } +} diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/Spellbook.java b/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/Spellbook.java index 5bf42c7ed..918ce4664 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/Spellbook.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/Spellbook.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servicelayer.spellbook; import java.util.HashSet; @@ -6,6 +28,7 @@ import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.ManyToMany; @@ -13,7 +36,7 @@ import javax.persistence.OneToMany; import javax.persistence.Table; import com.iluwatar.servicelayer.common.BaseEntity; -import com.iluwatar.servicelayer.servicelayer.spell.Spell; +import com.iluwatar.servicelayer.spell.Spell; import com.iluwatar.servicelayer.wizard.Wizard; /** @@ -22,71 +45,71 @@ import com.iluwatar.servicelayer.wizard.Wizard; * */ @Entity -@Table(name="SPELLBOOK") +@Table(name = "SPELLBOOK") public class Spellbook extends BaseEntity { - - public Spellbook() { - spells = new HashSet(); - wizards = new HashSet(); - } - - public Spellbook(String name) { - this(); - this.name = name; - } - @Id - @GeneratedValue - @Column(name = "SPELLBOOK_ID") - private Long id; + @Id + @GeneratedValue + @Column(name = "SPELLBOOK_ID") + private Long id; - public Long getId() { - return id; - } + private String name; - public void setId(Long id) { - this.id = id; - } - - private String name; + @ManyToMany(mappedBy = "spellbooks", fetch = FetchType.EAGER) + private Set wizards; - @ManyToMany(mappedBy = "spellbooks") - private Set wizards; + @OneToMany(mappedBy = "spellbook", orphanRemoval = true, cascade = CascadeType.ALL) + private Set spells; - @OneToMany(mappedBy = "spellbook", orphanRemoval = true, cascade = CascadeType.ALL) - private Set spells; - - public String getName() { - return name; - } + public Spellbook() { + spells = new HashSet<>(); + wizards = new HashSet<>(); + } - public void setName(String name) { - this.name = name; - } + public Spellbook(String name) { + this(); + this.name = name; + } + + public Long getId() { + return id; + } - public Set getWizards() { - return wizards; - } + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } - public void setWizards(Set wizards) { - this.wizards = wizards; - } + public void setName(String name) { + this.name = name; + } - public Set getSpells() { - return spells; - } + public Set getWizards() { + return wizards; + } - public void setSpells(Set spells) { - this.spells = spells; - } + public void setWizards(Set wizards) { + this.wizards = wizards; + } - public void addSpell(Spell spell) { - spell.setSpellbook(this); - spells.add(spell); - } - - @Override - public String toString() { - return name; - } + public Set getSpells() { + return spells; + } + + public void setSpells(Set spells) { + this.spells = spells; + } + + public void addSpell(Spell spell) { + spell.setSpellbook(this); + spells.add(spell); + } + + @Override + public String toString() { + return name; + } } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/SpellbookDao.java b/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/SpellbookDao.java index 9731cdf2e..ddf52cd73 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/SpellbookDao.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/SpellbookDao.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servicelayer.spellbook; import com.iluwatar.servicelayer.common.Dao; @@ -8,7 +30,7 @@ import com.iluwatar.servicelayer.common.Dao; * */ public interface SpellbookDao extends Dao { - - Spellbook findByName(String name); + + Spellbook findByName(String name); } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/SpellbookDaoImpl.java b/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/SpellbookDaoImpl.java index 5ab604621..3e2859d59 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/SpellbookDaoImpl.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/SpellbookDaoImpl.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servicelayer.spellbook; import org.hibernate.Criteria; @@ -14,28 +36,28 @@ import com.iluwatar.servicelayer.common.DaoBaseImpl; */ public class SpellbookDaoImpl extends DaoBaseImpl implements SpellbookDao { - @Override - public Spellbook findByName(String name) { - Session session = getSession(); - Transaction tx = null; - Spellbook result = null; - try { - tx = session.beginTransaction(); - Criteria criteria = session.createCriteria(persistentClass); - criteria.add(Restrictions.eq("name", name)); - result = (Spellbook) criteria.uniqueResult(); - result.getSpells().size(); - result.getWizards().size(); - tx.commit(); - } - catch (Exception e) { - if (tx!=null) tx.rollback(); - throw e; - } - finally { - session.close(); - } - return result; - } + @Override + public Spellbook findByName(String name) { + Session session = getSession(); + Transaction tx = null; + Spellbook result = null; + try { + tx = session.beginTransaction(); + Criteria criteria = session.createCriteria(persistentClass); + criteria.add(Restrictions.eq("name", name)); + result = (Spellbook) criteria.uniqueResult(); + result.getSpells().size(); + result.getWizards().size(); + tx.commit(); + } catch (Exception e) { + if (tx != null) { + tx.rollback(); + } + throw e; + } finally { + session.close(); + } + return result; + } } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/wizard/Wizard.java b/service-layer/src/main/java/com/iluwatar/servicelayer/wizard/Wizard.java index 10f811a3c..40e0b3ac7 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/wizard/Wizard.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/wizard/Wizard.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servicelayer.wizard; import java.util.HashSet; @@ -20,59 +42,59 @@ import com.iluwatar.servicelayer.spellbook.Spellbook; * */ @Entity -@Table(name="WIZARD") +@Table(name = "WIZARD") public class Wizard extends BaseEntity { - - public Wizard() { - spellbooks = new HashSet(); - } - - public Wizard(String name) { - this(); - this.name = name; - } - @Id - @GeneratedValue - @Column(name = "WIZARD_ID") - private Long id; + @Id + @GeneratedValue + @Column(name = "WIZARD_ID") + private Long id; - public Long getId() { - return id; - } + private String name; - public void setId(Long id) { - this.id = id; - } - - private String name; + @ManyToMany(cascade = CascadeType.ALL) + private Set spellbooks; - @ManyToMany(cascade = CascadeType.ALL) - private Set spellbooks; - - public String getName() { - return name; - } + public Wizard() { + spellbooks = new HashSet<>(); + } - public void setName(String name) { - this.name = name; - } + public Wizard(String name) { + this(); + this.name = name; + } + + public Long getId() { + return id; + } - public Set getSpellbooks() { - return spellbooks; - } + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } - public void setSpellbooks(Set spellbooks) { - this.spellbooks = spellbooks; - } + public void setName(String name) { + this.name = name; + } - public void addSpellbook(Spellbook spellbook) { - spellbook.getWizards().add(this); - spellbooks.add(spellbook); - } - - @Override - public String toString() { - return name; - } + public Set getSpellbooks() { + return spellbooks; + } + + public void setSpellbooks(Set spellbooks) { + this.spellbooks = spellbooks; + } + + public void addSpellbook(Spellbook spellbook) { + spellbook.getWizards().add(this); + spellbooks.add(spellbook); + } + + @Override + public String toString() { + return name; + } } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/wizard/WizardDao.java b/service-layer/src/main/java/com/iluwatar/servicelayer/wizard/WizardDao.java index f4f415d2f..ddf05fb04 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/wizard/WizardDao.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/wizard/WizardDao.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servicelayer.wizard; import com.iluwatar.servicelayer.common.Dao; @@ -8,7 +30,7 @@ import com.iluwatar.servicelayer.common.Dao; * */ public interface WizardDao extends Dao { - - Wizard findByName(String name); + + Wizard findByName(String name); } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/wizard/WizardDaoImpl.java b/service-layer/src/main/java/com/iluwatar/servicelayer/wizard/WizardDaoImpl.java index 28c3da85b..8243e3d5d 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/wizard/WizardDaoImpl.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/wizard/WizardDaoImpl.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servicelayer.wizard; import org.hibernate.Criteria; @@ -15,28 +37,28 @@ import com.iluwatar.servicelayer.spellbook.Spellbook; */ public class WizardDaoImpl extends DaoBaseImpl implements WizardDao { - @Override - public Wizard findByName(String name) { - Session session = getSession(); - Transaction tx = null; - Wizard result = null; - try { - tx = session.beginTransaction(); - Criteria criteria = session.createCriteria(persistentClass); - criteria.add(Restrictions.eq("name", name)); - result = (Wizard) criteria.uniqueResult(); - for (Spellbook s: result.getSpellbooks()) { - s.getSpells().size(); - } - tx.commit(); - } - catch (Exception e) { - if (tx!=null) tx.rollback(); - throw e; - } - finally { - session.close(); - } - return result; - } + @Override + public Wizard findByName(String name) { + Session session = getSession(); + Transaction tx = null; + Wizard result = null; + try { + tx = session.beginTransaction(); + Criteria criteria = session.createCriteria(persistentClass); + criteria.add(Restrictions.eq("name", name)); + result = (Wizard) criteria.uniqueResult(); + for (Spellbook s : result.getSpellbooks()) { + s.getSpells().size(); + } + tx.commit(); + } catch (Exception e) { + if (tx != null) { + tx.rollback(); + } + throw e; + } finally { + session.close(); + } + return result; + } } diff --git a/service-layer/src/test/java/com/iluwatar/servicelayer/app/AppTest.java b/service-layer/src/test/java/com/iluwatar/servicelayer/app/AppTest.java index aabeb1a6c..3626c3339 100644 --- a/service-layer/src/test/java/com/iluwatar/servicelayer/app/AppTest.java +++ b/service-layer/src/test/java/com/iluwatar/servicelayer/app/AppTest.java @@ -1,14 +1,48 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servicelayer.app; +import com.iluwatar.servicelayer.hibernate.HibernateUtil; + +import org.junit.After; import org.junit.Test; -import com.iluwatar.servicelayer.app.App; - +/** + * + * Application test + * + */ public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } + + @Test + public void test() { + String[] args = {}; + App.main(args); + } + + @After + public void tearDown() throws Exception { + HibernateUtil.dropSession(); + } + } diff --git a/service-layer/src/test/java/com/iluwatar/servicelayer/common/BaseDaoTest.java b/service-layer/src/test/java/com/iluwatar/servicelayer/common/BaseDaoTest.java new file mode 100644 index 000000000..789ded428 --- /dev/null +++ b/service-layer/src/test/java/com/iluwatar/servicelayer/common/BaseDaoTest.java @@ -0,0 +1,145 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.servicelayer.common; + +import com.iluwatar.servicelayer.hibernate.HibernateUtil; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +/** + * Date: 12/28/15 - 10:53 PM + * + * @author Jeroen Meulemeester + */ +public abstract class BaseDaoTest> { + + /** + * The number of entities stored before each test + */ + private static final int INITIAL_COUNT = 5; + + /** + * The unique id generator, shared between all entities + */ + private static final AtomicInteger ID_GENERATOR = new AtomicInteger(); + + /** + * Factory, used to create new entity instances with the given name + */ + private final Function factory; + + /** + * The tested data access object + */ + private final D dao; + + /** + * Create a new test using the given factory and dao + * + * @param factory The factory, used to create new entity instances with the given name + * @param dao The tested data access object + */ + public BaseDaoTest(final Function factory, final D dao) { + this.factory = factory; + this.dao = dao; + } + + @Before + public void setUp() throws Exception { + for (int i = 0; i < INITIAL_COUNT; i++) { + final String className = dao.persistentClass.getSimpleName(); + final String entityName = String.format("%s%d", className, ID_GENERATOR.incrementAndGet()); + this.dao.persist(this.factory.apply(entityName)); + } + } + + @After + public void tearDown() throws Exception { + HibernateUtil.dropSession(); + } + + protected final D getDao() { + return this.dao; + } + + @Test + public void testFind() throws Exception { + final List all = this.dao.findAll(); + for (final E entity : all) { + final E byId = this.dao.find(entity.getId()); + assertNotNull(byId); + assertEquals(byId.getId(), byId.getId()); + } + } + + @Test + public void testDelete() throws Exception { + final List originalEntities = this.dao.findAll(); + this.dao.delete(originalEntities.get(1)); + this.dao.delete(originalEntities.get(2)); + + final List entitiesLeft = this.dao.findAll(); + assertNotNull(entitiesLeft); + assertEquals(INITIAL_COUNT - 2, entitiesLeft.size()); + } + + @Test + public void testFindAll() throws Exception { + final List all = this.dao.findAll(); + assertNotNull(all); + assertEquals(INITIAL_COUNT, all.size()); + } + + @Test + public void testSetId() throws Exception { + final E entity = this.factory.apply("name"); + assertNull(entity.getId()); + + final Long expectedId = Long.valueOf(1); + entity.setId(expectedId); + assertEquals(expectedId, entity.getId()); + } + + @Test + public void testSetName() throws Exception { + final E entity = this.factory.apply("name"); + assertEquals("name", entity.getName()); + assertEquals("name", entity.toString()); + + final String expectedName = "new name"; + entity.setName(expectedName); + assertEquals(expectedName, entity.getName()); + assertEquals(expectedName, entity.toString()); + } + +} \ No newline at end of file diff --git a/service-layer/src/test/java/com/iluwatar/servicelayer/magic/MagicServiceImplTest.java b/service-layer/src/test/java/com/iluwatar/servicelayer/magic/MagicServiceImplTest.java new file mode 100644 index 000000000..dddc46916 --- /dev/null +++ b/service-layer/src/test/java/com/iluwatar/servicelayer/magic/MagicServiceImplTest.java @@ -0,0 +1,160 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.servicelayer.magic; + +import com.iluwatar.servicelayer.spell.Spell; +import com.iluwatar.servicelayer.spell.SpellDao; +import com.iluwatar.servicelayer.spellbook.Spellbook; +import com.iluwatar.servicelayer.spellbook.SpellbookDao; +import com.iluwatar.servicelayer.wizard.Wizard; +import com.iluwatar.servicelayer.wizard.WizardDao; + +import org.junit.Test; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +/** + * Date: 12/29/15 - 12:06 AM + * + * @author Jeroen Meulemeester + */ +public class MagicServiceImplTest { + + @Test + public void testFindAllWizards() throws Exception { + final WizardDao wizardDao = mock(WizardDao.class); + final SpellbookDao spellbookDao = mock(SpellbookDao.class); + final SpellDao spellDao = mock(SpellDao.class); + + final MagicServiceImpl service = new MagicServiceImpl(wizardDao, spellbookDao, spellDao); + verifyZeroInteractions(wizardDao, spellbookDao, spellDao); + + service.findAllWizards(); + verify(wizardDao).findAll(); + verifyNoMoreInteractions(wizardDao, spellbookDao, spellDao); + } + + @Test + public void testFindAllSpellbooks() throws Exception { + final WizardDao wizardDao = mock(WizardDao.class); + final SpellbookDao spellbookDao = mock(SpellbookDao.class); + final SpellDao spellDao = mock(SpellDao.class); + + final MagicServiceImpl service = new MagicServiceImpl(wizardDao, spellbookDao, spellDao); + verifyZeroInteractions(wizardDao, spellbookDao, spellDao); + + service.findAllSpellbooks(); + verify(spellbookDao).findAll(); + verifyNoMoreInteractions(wizardDao, spellbookDao, spellDao); + } + + @Test + public void testFindAllSpells() throws Exception { + final WizardDao wizardDao = mock(WizardDao.class); + final SpellbookDao spellbookDao = mock(SpellbookDao.class); + final SpellDao spellDao = mock(SpellDao.class); + + final MagicServiceImpl service = new MagicServiceImpl(wizardDao, spellbookDao, spellDao); + verifyZeroInteractions(wizardDao, spellbookDao, spellDao); + + service.findAllSpells(); + verify(spellDao).findAll(); + verifyNoMoreInteractions(wizardDao, spellbookDao, spellDao); + } + + @Test + public void testFindWizardsWithSpellbook() throws Exception { + final String bookname = "bookname"; + final Spellbook spellbook = mock(Spellbook.class); + final Set wizards = new HashSet<>(); + wizards.add(mock(Wizard.class)); + wizards.add(mock(Wizard.class)); + wizards.add(mock(Wizard.class)); + + when(spellbook.getWizards()).thenReturn(wizards); + + final SpellbookDao spellbookDao = mock(SpellbookDao.class); + when(spellbookDao.findByName(eq(bookname))).thenReturn(spellbook); + + final WizardDao wizardDao = mock(WizardDao.class); + final SpellDao spellDao = mock(SpellDao.class); + + + final MagicServiceImpl service = new MagicServiceImpl(wizardDao, spellbookDao, spellDao); + verifyZeroInteractions(wizardDao, spellbookDao, spellDao, spellbook); + + final List result = service.findWizardsWithSpellbook(bookname); + verify(spellbookDao).findByName(eq(bookname)); + verify(spellbook).getWizards(); + + assertNotNull(result); + assertEquals(3, result.size()); + + verifyNoMoreInteractions(wizardDao, spellbookDao, spellDao); + } + + @Test + public void testFindWizardsWithSpell() throws Exception { + final Set wizards = new HashSet<>(); + wizards.add(mock(Wizard.class)); + wizards.add(mock(Wizard.class)); + wizards.add(mock(Wizard.class)); + + final Spellbook spellbook = mock(Spellbook.class); + when(spellbook.getWizards()).thenReturn(wizards); + + final SpellbookDao spellbookDao = mock(SpellbookDao.class); + final WizardDao wizardDao = mock(WizardDao.class); + + final Spell spell = mock(Spell.class); + when(spell.getSpellbook()).thenReturn(spellbook); + + final String spellName = "spellname"; + final SpellDao spellDao = mock(SpellDao.class); + when(spellDao.findByName(eq(spellName))).thenReturn(spell); + + final MagicServiceImpl service = new MagicServiceImpl(wizardDao, spellbookDao, spellDao); + verifyZeroInteractions(wizardDao, spellbookDao, spellDao, spellbook); + + final List result = service.findWizardsWithSpell(spellName); + verify(spellDao).findByName(eq(spellName)); + verify(spellbook).getWizards(); + + assertNotNull(result); + assertEquals(3, result.size()); + + verifyNoMoreInteractions(wizardDao, spellbookDao, spellDao); + } + +} diff --git a/service-layer/src/test/java/com/iluwatar/servicelayer/spell/SpellDaoImplTest.java b/service-layer/src/test/java/com/iluwatar/servicelayer/spell/SpellDaoImplTest.java new file mode 100644 index 000000000..892ec6d2e --- /dev/null +++ b/service-layer/src/test/java/com/iluwatar/servicelayer/spell/SpellDaoImplTest.java @@ -0,0 +1,57 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.servicelayer.spell; + +import com.iluwatar.servicelayer.common.BaseDaoTest; + +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/28/15 - 11:02 PM + * + * @author Jeroen Meulemeester + */ +public class SpellDaoImplTest extends BaseDaoTest { + + public SpellDaoImplTest() { + super(Spell::new, new SpellDaoImpl()); + } + + @Test + public void testFindByName() throws Exception { + final SpellDaoImpl dao = getDao(); + final List allSpells = dao.findAll(); + for (final Spell spell : allSpells) { + final Spell spellByName = dao.findByName(spell.getName()); + assertNotNull(spellByName); + assertEquals(spell.getId(), spellByName.getId()); + assertEquals(spell.getName(), spellByName.getName()); + } + } + +} diff --git a/service-layer/src/test/java/com/iluwatar/servicelayer/spellbook/SpellbookDaoImplTest.java b/service-layer/src/test/java/com/iluwatar/servicelayer/spellbook/SpellbookDaoImplTest.java new file mode 100644 index 000000000..957c07bea --- /dev/null +++ b/service-layer/src/test/java/com/iluwatar/servicelayer/spellbook/SpellbookDaoImplTest.java @@ -0,0 +1,57 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.servicelayer.spellbook; + +import com.iluwatar.servicelayer.common.BaseDaoTest; + +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/28/15 - 11:44 PM + * + * @author Jeroen Meulemeester + */ +public class SpellbookDaoImplTest extends BaseDaoTest { + + public SpellbookDaoImplTest() { + super(Spellbook::new, new SpellbookDaoImpl()); + } + + @Test + public void testFindByName() throws Exception { + final SpellbookDaoImpl dao = getDao(); + final List allBooks = dao.findAll(); + for (final Spellbook book : allBooks) { + final Spellbook spellByName = dao.findByName(book.getName()); + assertNotNull(spellByName); + assertEquals(book.getId(), spellByName.getId()); + assertEquals(book.getName(), spellByName.getName()); + } + } + +} diff --git a/service-layer/src/test/java/com/iluwatar/servicelayer/wizard/WizardDaoImplTest.java b/service-layer/src/test/java/com/iluwatar/servicelayer/wizard/WizardDaoImplTest.java new file mode 100644 index 000000000..8649ee0a0 --- /dev/null +++ b/service-layer/src/test/java/com/iluwatar/servicelayer/wizard/WizardDaoImplTest.java @@ -0,0 +1,57 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.servicelayer.wizard; + +import com.iluwatar.servicelayer.common.BaseDaoTest; + +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/28/15 - 11:46 PM + * + * @author Jeroen Meulemeester + */ +public class WizardDaoImplTest extends BaseDaoTest { + + public WizardDaoImplTest() { + super(Wizard::new, new WizardDaoImpl()); + } + + @Test + public void testFindByName() throws Exception { + final WizardDaoImpl dao = getDao(); + final List allWizards = dao.findAll(); + for (final Wizard spell : allWizards) { + final Wizard byName = dao.findByName(spell.getName()); + assertNotNull(byName); + assertEquals(spell.getId(), byName.getId()); + assertEquals(spell.getName(), byName.getName()); + } + } + +} diff --git a/service-locator/README.md b/service-locator/README.md new file mode 100644 index 000000000..af4d8c3ac --- /dev/null +++ b/service-locator/README.md @@ -0,0 +1,37 @@ +--- +layout: pattern +title: Service Locator +folder: service-locator +permalink: /patterns/service-locator/ +categories: Structural +tags: + - Java + - Difficulty-Beginner + - Performance +--- + +## Intent +Encapsulate the processes involved in obtaining a service with a +strong abstraction layer. + +![alt text](./etc/service-locator.png "Service Locator") + +## Applicability +The service locator pattern is applicable whenever we want +to locate/fetch various services using JNDI which, typically, is a redundant +and expensive lookup. The service Locator pattern addresses this expensive +lookup by making use of caching techniques ie. for the very first time a +particular service is requested, the service Locator looks up in JNDI, fetched +the relevant service and then finally caches this service object. Now, further +lookups of the same service via Service Locator is done in its cache which +improves the performance of application to great extent. + +## Typical Use Case + +* when network hits are expensive and time consuming +* lookups of services are done quite frequently +* large number of services are being used + +## Credits + +* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) diff --git a/service-locator/pom.xml b/service-locator/pom.xml index a0a84a43c..0f17b3dcc 100644 --- a/service-locator/pom.xml +++ b/service-locator/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT service-locator diff --git a/service-locator/src/main/java/com/iluwatar/servicelocator/App.java b/service-locator/src/main/java/com/iluwatar/servicelocator/App.java index 57f188731..72612fb21 100644 --- a/service-locator/src/main/java/com/iluwatar/servicelocator/App.java +++ b/service-locator/src/main/java/com/iluwatar/servicelocator/App.java @@ -1,20 +1,56 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servicelocator; /** - * Service locator pattern, used to lookup jndi services - * and cache them for subsequent requests. - * + * + * The Service Locator pattern is a design pattern used in software development to encapsulate the + * processes involved in obtaining a service with a strong abstraction layer. This pattern uses a + * central registry known as the "service locator", which on request returns the information + * necessary to perform a certain task. + *

+ * In this example we use the Service locator pattern to lookup JNDI-services and cache them for + * subsequent requests. + *

+ * * @author saifasif + * */ public class App { - public static void main(String[] args) { - Service service = ServiceLocator.getService("jndi/serviceA"); - service.execute(); - service = ServiceLocator.getService("jndi/serviceB"); - service.execute(); - service = ServiceLocator.getService("jndi/serviceA"); - service.execute(); - service = ServiceLocator.getService("jndi/serviceA"); - service.execute(); - } + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + Service service = ServiceLocator.getService("jndi/serviceA"); + service.execute(); + service = ServiceLocator.getService("jndi/serviceB"); + service.execute(); + service = ServiceLocator.getService("jndi/serviceA"); + service.execute(); + service = ServiceLocator.getService("jndi/serviceA"); + service.execute(); + } } diff --git a/service-locator/src/main/java/com/iluwatar/servicelocator/InitContext.java b/service-locator/src/main/java/com/iluwatar/servicelocator/InitContext.java index 64b65ea1d..8063fc818 100644 --- a/service-locator/src/main/java/com/iluwatar/servicelocator/InitContext.java +++ b/service-locator/src/main/java/com/iluwatar/servicelocator/InitContext.java @@ -1,29 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servicelocator; /** - * For JNDI lookup of services from the web.xml. Will match name of the service name that - * is being requested and return a newly created service object with the name + * For JNDI lookup of services from the web.xml. Will match name of the service name that is being + * requested and return a newly created service object with the name * * @author saifasif */ public class InitContext { - /** - * Perform the lookup based on the service name. The returned object will need to be - * casted into a {@link Service} - * - * @param serviceName a string - * @return an {@link Object} - */ - public Object lookup(String serviceName) { - if (serviceName.equals("jndi/serviceA")) { - System.out.println("Looking up service A and creating new service for A"); - return new ServiceImpl("jndi/serviceA"); - } else if (serviceName.equals("jndi/serviceB")) { - System.out.println("Looking up service B and creating new service for B"); - return new ServiceImpl("jndi/serviceB"); - } else { - return null; - } + /** + * Perform the lookup based on the service name. The returned object will need to be casted into a + * {@link Service} + * + * @param serviceName a string + * @return an {@link Object} + */ + public Object lookup(String serviceName) { + if (serviceName.equals("jndi/serviceA")) { + System.out.println("Looking up service A and creating new service for A"); + return new ServiceImpl("jndi/serviceA"); + } else if (serviceName.equals("jndi/serviceB")) { + System.out.println("Looking up service B and creating new service for B"); + return new ServiceImpl("jndi/serviceB"); + } else { + return null; } + } } diff --git a/service-locator/src/main/java/com/iluwatar/servicelocator/Service.java b/service-locator/src/main/java/com/iluwatar/servicelocator/Service.java index dbb268159..4f5890bba 100644 --- a/service-locator/src/main/java/com/iluwatar/servicelocator/Service.java +++ b/service-locator/src/main/java/com/iluwatar/servicelocator/Service.java @@ -1,28 +1,48 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servicelocator; /** - * This is going to be the parent service interface which we will - * use to create our services. All services will have a - *

  • service name
  • - *
  • unique id
  • - *
  • execution work flow
  • + * This is going to be the parent service interface which we will use to create our services. All + * services will have a
  • service name
  • unique id
  • execution work flow
  • + * * @author saifasif * */ public interface Service { - - /* - * The human readable name of the service - */ - String getName(); - - /* - * Unique ID of the particular service - */ - int getId(); - - /* - * The workflow method that defines what this service does - */ - void execute(); + + /* + * The human readable name of the service + */ + String getName(); + + /* + * Unique ID of the particular service + */ + int getId(); + + /* + * The workflow method that defines what this service does + */ + void execute(); } diff --git a/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceCache.java b/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceCache.java index c48e4c7af..7e2169fcb 100644 --- a/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceCache.java +++ b/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceCache.java @@ -1,47 +1,70 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servicelocator; import java.util.HashMap; import java.util.Map; /** - * The service cache implementation which will cache services that are being created. - * On first hit, the cache will be empty and thus any service that is being requested, will be - * created fresh and then placed into the cache map. On next hit, if same service name will - * be requested, it will be returned from the cache + * The service cache implementation which will cache services that are being created. On first hit, + * the cache will be empty and thus any service that is being requested, will be created fresh and + * then placed into the cache map. On next hit, if same service name will be requested, it will be + * returned from the cache * * @author saifasif */ public class ServiceCache { - private final Map serviceCache; + private final Map serviceCache; - public ServiceCache() { - serviceCache = new HashMap(); - } + public ServiceCache() { + serviceCache = new HashMap<>(); + } - /** - * Get the service from the cache. null if no service is found matching the name - * - * @param serviceName a string - * @return {@link Service} - */ - public Service getService(String serviceName) { - Service cachedService = null; - for (String serviceJndiName : serviceCache.keySet()) { - if (serviceJndiName.equals(serviceName)) { - cachedService = serviceCache.get(serviceJndiName); - System.out.println("(cache call) Fetched service " + cachedService.getName() + "(" + cachedService.getId() + ") from cache... !"); - } - } - return cachedService; + /** + * Get the service from the cache. null if no service is found matching the name + * + * @param serviceName a string + * @return {@link Service} + */ + public Service getService(String serviceName) { + Service cachedService = null; + for (String serviceJndiName : serviceCache.keySet()) { + if (serviceJndiName.equals(serviceName)) { + cachedService = serviceCache.get(serviceJndiName); + System.out.println("(cache call) Fetched service " + cachedService.getName() + "(" + + cachedService.getId() + ") from cache... !"); + } } + return cachedService; + } - /** - * Adds the service into the cache map - * - * @param newService a {@link Service} - */ - public void addService(Service newService) { - serviceCache.put(newService.getName(), newService); - } + /** + * Adds the service into the cache map + * + * @param newService a {@link Service} + */ + public void addService(Service newService) { + serviceCache.put(newService.getName(), newService); + } } diff --git a/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceImpl.java b/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceImpl.java index ec7ab215b..543fb8480 100644 --- a/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceImpl.java +++ b/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceImpl.java @@ -1,37 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servicelocator; /** - * This is a single service implementation of a sample service. This is the actual - * service that will process the request. The reference for this service is to - * be looked upon in the JNDI server that can be set in the web.xml deployment descriptor + * This is a single service implementation of a sample service. This is the actual service that will + * process the request. The reference for this service is to be looked upon in the JNDI server that + * can be set in the web.xml deployment descriptor * * @author saifasif */ public class ServiceImpl implements Service { - private final String serviceName; - private final int id; + private final String serviceName; + private final int id; - public ServiceImpl(String serviceName) { - // set the service name - this.serviceName = serviceName; + /** + * Constructor + */ + public ServiceImpl(String serviceName) { + // set the service name + this.serviceName = serviceName; - // Generate a random id to this service object - this.id = (int) Math.floor(Math.random() * 1000) + 1; - } + // Generate a random id to this service object + this.id = (int) Math.floor(Math.random() * 1000) + 1; + } - @Override - public String getName() { - return serviceName; - } + @Override + public String getName() { + return serviceName; + } - @Override - public int getId() { - return id; - } + @Override + public int getId() { + return id; + } - @Override - public void execute() { - System.out.println("Service " + getName() + " is now executing with id " + getId()); - } + @Override + public void execute() { + System.out.println("Service " + getName() + " is now executing with id " + getId()); + } } diff --git a/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceLocator.java b/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceLocator.java index 95df24926..4d356a567 100644 --- a/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceLocator.java +++ b/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceLocator.java @@ -1,36 +1,63 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.servicelocator; /** - * The service locator module. - * Will fetch service from cache, otherwise creates a fresh service and update cache + * The service locator module. Will fetch service from cache, otherwise creates a fresh service and + * update cache * * @author saifasif */ -public class ServiceLocator { +public final class ServiceLocator { - private static ServiceCache serviceCache = new ServiceCache(); + private static ServiceCache serviceCache = new ServiceCache(); - /** - * Fetch the service with the name param from the cache first, - * if no service is found, lookup the service from the {@link InitContext} and - * then add the newly created service into the cache map for future requests. - * - * @param serviceJndiName a string - * @return {@link Service} - */ - public static Service getService(String serviceJndiName) { - Service serviceObj = serviceCache.getService(serviceJndiName); - if (serviceObj != null) { - return serviceObj; - } else { - /* - * If we are unable to retrive anything from cache, then - * lookup the service and add it in the cache map - */ - InitContext ctx = new InitContext(); - serviceObj = (Service) ctx.lookup(serviceJndiName); - serviceCache.addService(serviceObj); - return serviceObj; - } + private ServiceLocator() { + } + + /** + * Fetch the service with the name param from the cache first, if no service is found, lookup the + * service from the {@link InitContext} and then add the newly created service into the cache map + * for future requests. + * + * @param serviceJndiName a string + * @return {@link Service} + */ + public static Service getService(String serviceJndiName) { + Service serviceObj = serviceCache.getService(serviceJndiName); + if (serviceObj != null) { + return serviceObj; + } else { + /* + * If we are unable to retrive anything from cache, then lookup the service and add it in the + * cache map + */ + InitContext ctx = new InitContext(); + serviceObj = (Service) ctx.lookup(serviceJndiName); + if (serviceObj != null) { // Only cache a service if it actually exists + serviceCache.addService(serviceObj); + } + return serviceObj; } + } } diff --git a/service-locator/src/test/java/com/iluwatar/servicelocator/AppTest.java b/service-locator/src/test/java/com/iluwatar/servicelocator/AppTest.java index d6af23f2d..40e3820e9 100644 --- a/service-locator/src/test/java/com/iluwatar/servicelocator/AppTest.java +++ b/service-locator/src/test/java/com/iluwatar/servicelocator/AppTest.java @@ -1,14 +1,39 @@ -package com.iluwatar.servicelocator; - -import org.junit.Test; - -import com.iluwatar.servicelocator.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.servicelocator; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/service-locator/src/test/java/com/iluwatar/servicelocator/ServiceLocatorTest.java b/service-locator/src/test/java/com/iluwatar/servicelocator/ServiceLocatorTest.java new file mode 100644 index 000000000..b9f25ff44 --- /dev/null +++ b/service-locator/src/test/java/com/iluwatar/servicelocator/ServiceLocatorTest.java @@ -0,0 +1,68 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.servicelocator; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +/** + * Date: 12/29/15 - 19:07 PM + * + * @author Jeroen Meulemeester + */ +public class ServiceLocatorTest { + + /** + * Verify if we just receive 'null' when requesting a non-existing service + */ + @Test + public void testGetNonExistentService() { + assertNull(ServiceLocator.getService("fantastic/unicorn/service")); + assertNull(ServiceLocator.getService("another/fantastic/unicorn/service")); + } + + /** + * Verify if we get the same cached instance when requesting the same service twice + */ + @Test + public void testServiceCache() { + final String[] serviceNames = new String[]{ + "jndi/serviceA", "jndi/serviceB" + }; + + for (final String serviceName : serviceNames) { + final Service service = ServiceLocator.getService(serviceName); + assertNotNull(service); + assertEquals(serviceName, service.getName()); + assertTrue(service.getId() > 0); // The id is generated randomly, but the minimum value is '1' + assertSame(service, ServiceLocator.getService(serviceName)); + } + + } + +} \ No newline at end of file diff --git a/singleton/README.md b/singleton/README.md new file mode 100644 index 000000000..2a481f5c8 --- /dev/null +++ b/singleton/README.md @@ -0,0 +1,38 @@ +--- +layout: pattern +title: Singleton +folder: singleton +permalink: /patterns/singleton/ +categories: Creational +tags: + - Java + - Gang Of Four + - Difficulty-Beginner +--- + +## Intent +Ensure a class only has one instance, and provide a global point of +access to it. + +![alt text](./etc/singleton_1.png "Singleton") + +## Applicability +Use the Singleton pattern when + +* there must be exactly one instance of a class, and it must be accessible to clients from a well-known access point +* when the sole instance should be extensible by subclassing, and clients should be able to use an extended instance without modifying their code + +## Typical Use Case + +* the logging class +* managing a connection to a database +* file manager + +## Real world examples + +* [java.lang.Runtime#getRuntime()](http://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#getRuntime%28%29) + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) +* [Effective Java (2nd Edition)](http://www.amazon.com/Effective-Java-Edition-Joshua-Bloch/dp/0321356683) diff --git a/singleton/pom.xml b/singleton/pom.xml index 1db1ff382..ab9405c5e 100644 --- a/singleton/pom.xml +++ b/singleton/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT singleton diff --git a/singleton/src/main/java/com/iluwatar/singleton/App.java b/singleton/src/main/java/com/iluwatar/singleton/App.java index d14088ca2..4b505085a 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/App.java +++ b/singleton/src/main/java/com/iluwatar/singleton/App.java @@ -1,51 +1,104 @@ -package com.iluwatar.singleton; - -/** - * - * Singleton pattern ensures that the class (IvoryTower) can have only one - * existing instance per java classloader instance and provides global access to it. - * - * http://stackoverflow.com/questions/70689/what-is-an-efficient-way-to-implement-a-singleton-pattern-in-java - * - * The risk of this pattern is that bugs resulting from setting a singleton up in a distributed environment can - * be tricky to debug, since it will work fine if you debug with a single classloader. Additionally, these - * problems can crop up a while after the implementation of a singleton, since they may start out synchronous and - * only become async with time, so you it may not be clear why you are seeing certain changes in behaviour. - * - * http://stackoverflow.com/questions/17721263/singleton-across-jvm-or-application-instance-or-tomcat-instance - */ -public class App { - - public static void main(String[] args) { - - // eagerly initialized singleton - IvoryTower ivoryTower1 = IvoryTower.getInstance(); - IvoryTower ivoryTower2 = IvoryTower.getInstance(); - System.out.println("ivoryTower1=" + ivoryTower1); - System.out.println("ivoryTower2=" + ivoryTower2); - - // lazily initialized singleton - ThreadSafeLazyLoadedIvoryTower threadSafeIvoryTower1 = ThreadSafeLazyLoadedIvoryTower - .getInstance(); - ThreadSafeLazyLoadedIvoryTower threadSafeIvoryTower2 = ThreadSafeLazyLoadedIvoryTower - .getInstance(); - System.out.println("threadSafeIvoryTower1=" + threadSafeIvoryTower1); - System.out.println("threadSafeIvoryTower2=" + threadSafeIvoryTower2); - - // enum singleton - EnumIvoryTower enumIvoryTower1 = EnumIvoryTower.INSTANCE; - EnumIvoryTower enumIvoryTower2 = EnumIvoryTower.INSTANCE; - System.out.println("enumIvoryTower1=" + enumIvoryTower1); - System.out.println("enumIvoryTower2=" + enumIvoryTower2); - - InitializingOnDemandHolderIdiom demandHolderIdiom = InitializingOnDemandHolderIdiom.getInstance(); - System.out.println(demandHolderIdiom); - InitializingOnDemandHolderIdiom demandHolderIdiom2 = InitializingOnDemandHolderIdiom.getInstance(); - System.out.println(demandHolderIdiom2); - - ThreadSafeDoubleCheckLocking dcl1 = ThreadSafeDoubleCheckLocking.getInstance(); - System.out.println(dcl1); - ThreadSafeDoubleCheckLocking dcl2 = ThreadSafeDoubleCheckLocking.getInstance(); - System.out.println(dcl2); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.singleton; + +/** + * Singleton pattern ensures that the class can have only one existing instance per Java classloader + * instance and provides global access to it. + *

    + * One of the risks of this pattern is that bugs resulting from setting a singleton up in a + * distributed environment can be tricky to debug, since it will work fine if you debug with a + * single classloader. Additionally, these problems can crop up a while after the implementation of + * a singleton, since they may start out synchronous and only become async with time, so you it may + * not be clear why you are seeing certain changes in behaviour. + *

    + * There are many ways to implement the Singleton. The first one is the eagerly initialized instance + * in {@link IvoryTower}. Eager initialization implies that the implementation is thread safe. If + * you can afford giving up control of the instantiation moment, then this implementation will suit + * you fine. + *

    + * The other option to implement eagerly initialized Singleton is enum based Singleton. The example + * is found in {@link EnumIvoryTower}. At first glance the code looks short and simple. However, you + * should be aware of the downsides including committing to implementation strategy, extending the + * enum class, serializability and restrictions to coding. These are extensively discussed in Stack + * Overflow: + * http://programmers.stackexchange.com/questions/179386/what-are-the-downsides-of-implementing + * -a-singleton-with-javas-enum + *

    + * {@link ThreadSafeLazyLoadedIvoryTower} is a Singleton implementation that is initialized on + * demand. The downside is that it is very slow to access since the whole access method is + * synchronized. + *

    + * Another Singleton implementation that is initialized on demand is found in + * {@link ThreadSafeDoubleCheckLocking}. It is somewhat faster than + * {@link ThreadSafeLazyLoadedIvoryTower} since it doesn't synchronize the whole access method but + * only the method internals on specific conditions. + *

    + * Yet another way to implement thread safe lazily initialized Singleton can be found in + * {@link InitializingOnDemandHolderIdiom}. However, this implementation requires at least Java 8 + * API level to work. + */ +public class App { + + /** + * Program entry point. + * + * @param args command line args + */ + public static void main(String[] args) { + + // eagerly initialized singleton + IvoryTower ivoryTower1 = IvoryTower.getInstance(); + IvoryTower ivoryTower2 = IvoryTower.getInstance(); + System.out.println("ivoryTower1=" + ivoryTower1); + System.out.println("ivoryTower2=" + ivoryTower2); + + // lazily initialized singleton + ThreadSafeLazyLoadedIvoryTower threadSafeIvoryTower1 = + ThreadSafeLazyLoadedIvoryTower.getInstance(); + ThreadSafeLazyLoadedIvoryTower threadSafeIvoryTower2 = + ThreadSafeLazyLoadedIvoryTower.getInstance(); + System.out.println("threadSafeIvoryTower1=" + threadSafeIvoryTower1); + System.out.println("threadSafeIvoryTower2=" + threadSafeIvoryTower2); + + // enum singleton + EnumIvoryTower enumIvoryTower1 = EnumIvoryTower.INSTANCE; + EnumIvoryTower enumIvoryTower2 = EnumIvoryTower.INSTANCE; + System.out.println("enumIvoryTower1=" + enumIvoryTower1); + System.out.println("enumIvoryTower2=" + enumIvoryTower2); + + // double checked locking + ThreadSafeDoubleCheckLocking dcl1 = ThreadSafeDoubleCheckLocking.getInstance(); + System.out.println(dcl1); + ThreadSafeDoubleCheckLocking dcl2 = ThreadSafeDoubleCheckLocking.getInstance(); + System.out.println(dcl2); + + // initialize on demand holder idiom + InitializingOnDemandHolderIdiom demandHolderIdiom = + InitializingOnDemandHolderIdiom.getInstance(); + System.out.println(demandHolderIdiom); + InitializingOnDemandHolderIdiom demandHolderIdiom2 = + InitializingOnDemandHolderIdiom.getInstance(); + System.out.println(demandHolderIdiom2); + } +} diff --git a/singleton/src/main/java/com/iluwatar/singleton/EnumIvoryTower.java b/singleton/src/main/java/com/iluwatar/singleton/EnumIvoryTower.java index 8f9895924..eea1cd8cb 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/EnumIvoryTower.java +++ b/singleton/src/main/java/com/iluwatar/singleton/EnumIvoryTower.java @@ -1,17 +1,36 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.singleton; /** - * - * Enum Singleton class. - * Effective Java 2nd Edition (Joshua Bloch) p. 18 - * + * Enum based singleton implementation. Effective Java 2nd Edition (Joshua Bloch) p. 18 */ public enum EnumIvoryTower { - - INSTANCE; - @Override - public String toString() { - return getDeclaringClass().getCanonicalName() + "@" + hashCode(); - } + INSTANCE; + + @Override + public String toString() { + return getDeclaringClass().getCanonicalName() + "@" + hashCode(); + } } diff --git a/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java b/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java index 329cd6a7e..4aa6afe12 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java +++ b/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java @@ -1,36 +1,59 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.singleton; -import java.io.Serializable; - /** - * The Initialize-on-demand-holder idiom is a secure way of - * creating lazy initialize singleton Object in Java. - * refer to "The CERT Oracle Secure Coding Standard for Java" - * By Dhruv Mohindra, Robert C. Seacord p.378 - * - * Singleton objects usually are heavy to create and sometimes need to serialize them. - * This class also shows how to preserve singleton in Serialized version of singleton. - * - * @author mortezaadi@gmail.com + * The Initialize-on-demand-holder idiom is a secure way of creating a lazy initialized singleton + * object in Java. + *

    + * The technique is is as lazy as possible and works in all known versions of Java. It takes advantage + * of language guarantees about class initialization, and will therefore work correctly in all + * Java-compliant compilers and virtual machines. + *

    + * The inner class is referenced no earlier (and therefore loaded no earlier by the class loader) than + * the moment that getInstance() is called. Thus, this solution is thread-safe without requiring special + * language constructs (i.e. volatile or synchronized). * */ -public class InitializingOnDemandHolderIdiom implements Serializable{ +public final class InitializingOnDemandHolderIdiom { - private static final long serialVersionUID = 1L; + /** + * Private constructor. + */ + private InitializingOnDemandHolderIdiom() {} - private static class HelperHolder { - public static final InitializingOnDemandHolderIdiom INSTANCE = new InitializingOnDemandHolderIdiom(); - } - - public static InitializingOnDemandHolderIdiom getInstance() { - return HelperHolder.INSTANCE; - } - - private InitializingOnDemandHolderIdiom() { - } - - protected Object readResolve() { - return getInstance(); - } + /** + * @return Singleton instance + */ + public static InitializingOnDemandHolderIdiom getInstance() { + return HelperHolder.INSTANCE; + } + /** + * Provides the lazy-loaded Singleton instance. + */ + private static class HelperHolder { + public static final InitializingOnDemandHolderIdiom INSTANCE = + new InitializingOnDemandHolderIdiom(); + } } diff --git a/singleton/src/main/java/com/iluwatar/singleton/IvoryTower.java b/singleton/src/main/java/com/iluwatar/singleton/IvoryTower.java index 5d418aa54..1dbffa00b 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/IvoryTower.java +++ b/singleton/src/main/java/com/iluwatar/singleton/IvoryTower.java @@ -1,20 +1,48 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.singleton; /** - * - * Singleton class. - * Eagerly initialized static instance guarantees thread - * safety. - * + * Singleton class. Eagerly initialized static instance guarantees thread safety. */ -public class IvoryTower { +public final class IvoryTower { - private static IvoryTower instance = new IvoryTower(); + /** + * Static to class instance of the class. + */ + private static final IvoryTower INSTANCE = new IvoryTower(); - private IvoryTower() { - } + /** + * Private constructor so nobody can instantiate the class. + */ + private IvoryTower() {} - public static IvoryTower getInstance() { - return instance; - } + /** + * To be called by user to obtain instance of the class. + * + * @return instance of the singleton. + */ + public static IvoryTower getInstance() { + return INSTANCE; + } } diff --git a/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLocking.java b/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLocking.java index 352cac2e9..50203609c 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLocking.java +++ b/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLocking.java @@ -1,40 +1,67 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.singleton; /** * Double check locking - * + *

    * http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html - * + *

    * Broken under Java 1.4. - * @author mortezaadi@gmail.com * + * @author mortezaadi@gmail.com */ -public class ThreadSafeDoubleCheckLocking { - - private static volatile ThreadSafeDoubleCheckLocking INSTANCE; +public final class ThreadSafeDoubleCheckLocking { - /** - * private constructor to prevent client from instantiating. - * - */ - private ThreadSafeDoubleCheckLocking() { - //to prevent instantiating by Reflection call - if(INSTANCE != null) - throw new IllegalStateException("Already initialized."); - } - - public static ThreadSafeDoubleCheckLocking getInstance() { - //local variable increases performance by 25 percent - //Joshua Bloch "Effective Java, Second Edition", p. 283-284 - ThreadSafeDoubleCheckLocking result = INSTANCE; - if (result == null) { - synchronized (ThreadSafeDoubleCheckLocking.class) { - result = INSTANCE; - if (result == null) { - INSTANCE = result = new ThreadSafeDoubleCheckLocking(); - } - } - } - return result; - } + private static volatile ThreadSafeDoubleCheckLocking instance; + + /** + * private constructor to prevent client from instantiating. + */ + private ThreadSafeDoubleCheckLocking() { + // to prevent instantiating by Reflection call + if (instance != null) { + throw new IllegalStateException("Already initialized."); + } + } + + /** + * Public accessor. + * + * @return an instance of the class. + */ + public static ThreadSafeDoubleCheckLocking getInstance() { + // local variable increases performance by 25 percent + // Joshua Bloch "Effective Java, Second Edition", p. 283-284 + ThreadSafeDoubleCheckLocking result = instance; + if (result == null) { + synchronized (ThreadSafeDoubleCheckLocking.class) { + result = instance; + if (result == null) { + instance = result = new ThreadSafeDoubleCheckLocking(); + } + } + } + return result; + } } diff --git a/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java b/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java index f7184cfb0..472325786 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java +++ b/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java @@ -1,28 +1,49 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.singleton; /** - * - * Thread-safe Singleton class. - * The instance is lazily initialized and thus needs synchronization + * Thread-safe Singleton class. The instance is lazily initialized and thus needs synchronization * mechanism. * + * Note: if created by reflection then a singleton will not be created but multiple options in the + * same classloader */ -public class ThreadSafeLazyLoadedIvoryTower { +public final class ThreadSafeLazyLoadedIvoryTower { - private static ThreadSafeLazyLoadedIvoryTower instance = null; - - private ThreadSafeLazyLoadedIvoryTower() { - } + private static ThreadSafeLazyLoadedIvoryTower instance; - public synchronized static ThreadSafeLazyLoadedIvoryTower getInstance() { - /* - * The instance gets created only when it is called for first time. - * Lazy-loading - */ - if (instance == null) { - instance = new ThreadSafeLazyLoadedIvoryTower(); - } + private ThreadSafeLazyLoadedIvoryTower() {} - return instance; - } + /** + * The instance gets created only when it is called for first time. Lazy-loading + */ + public static synchronized ThreadSafeLazyLoadedIvoryTower getInstance() { + + if (instance == null) { + instance = new ThreadSafeLazyLoadedIvoryTower(); + } + + return instance; + } } diff --git a/singleton/src/test/java/com/iluwatar/singleton/AppTest.java b/singleton/src/test/java/com/iluwatar/singleton/AppTest.java index abe0440fd..c2def43a0 100644 --- a/singleton/src/test/java/com/iluwatar/singleton/AppTest.java +++ b/singleton/src/test/java/com/iluwatar/singleton/AppTest.java @@ -1,14 +1,39 @@ -package com.iluwatar.singleton; - -import org.junit.Test; - -import com.iluwatar.singleton.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.singleton; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/singleton/src/test/java/com/iluwatar/singleton/EnumIvoryTowerTest.java b/singleton/src/test/java/com/iluwatar/singleton/EnumIvoryTowerTest.java new file mode 100644 index 000000000..ff821c6eb --- /dev/null +++ b/singleton/src/test/java/com/iluwatar/singleton/EnumIvoryTowerTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.singleton; + +/** + * Date: 12/29/15 - 19:20 PM + * + * @author Jeroen Meulemeester + */ +public class EnumIvoryTowerTest extends SingletonTest { + + /** + * Create a new singleton test instance using the given 'getInstance' method + */ + public EnumIvoryTowerTest() { + super(() -> EnumIvoryTower.INSTANCE); + } + +} diff --git a/singleton/src/test/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiomTest.java b/singleton/src/test/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiomTest.java new file mode 100644 index 000000000..1aacb3d08 --- /dev/null +++ b/singleton/src/test/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiomTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.singleton; + +/** + * Date: 12/29/15 - 19:22 PM + * + * @author Jeroen Meulemeester + */ +public class InitializingOnDemandHolderIdiomTest extends SingletonTest { + + /** + * Create a new singleton test instance using the given 'getInstance' method + */ + public InitializingOnDemandHolderIdiomTest() { + super(InitializingOnDemandHolderIdiom::getInstance); + } + +} \ No newline at end of file diff --git a/singleton/src/test/java/com/iluwatar/singleton/IvoryTowerTest.java b/singleton/src/test/java/com/iluwatar/singleton/IvoryTowerTest.java new file mode 100644 index 000000000..67769d87d --- /dev/null +++ b/singleton/src/test/java/com/iluwatar/singleton/IvoryTowerTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.singleton; + +/** + * Date: 12/29/15 - 19:23 PM + * + * @author Jeroen Meulemeester + */ +public class IvoryTowerTest extends SingletonTest { + + /** + * Create a new singleton test instance using the given 'getInstance' method + */ + public IvoryTowerTest() { + super(IvoryTower::getInstance); + } + +} \ No newline at end of file diff --git a/singleton/src/test/java/com/iluwatar/singleton/SingletonTest.java b/singleton/src/test/java/com/iluwatar/singleton/SingletonTest.java new file mode 100644 index 000000000..0d9d0aee9 --- /dev/null +++ b/singleton/src/test/java/com/iluwatar/singleton/SingletonTest.java @@ -0,0 +1,110 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.singleton; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.function.Supplier; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; + +/** + * This class provides several test case that test singleton construction. + * + * The first proves that multiple calls to the singleton getInstance object are the same when called + * in the SAME thread. The second proves that multiple calls to the singleton getInstance object are + * the same when called in the DIFFERENT thread. + * + * Date: 12/29/15 - 19:25 PM + * + * @author Jeroen Meulemeester + * @author Richard Jones + */ +public abstract class SingletonTest { + + /** + * The singleton's getInstance method + */ + private final Supplier singletonInstanceMethod; + + /** + * Create a new singleton test instance using the given 'getInstance' method + * + * @param singletonInstanceMethod The singleton's getInstance method + */ + public SingletonTest(final Supplier singletonInstanceMethod) { + this.singletonInstanceMethod = singletonInstanceMethod; + } + + /** + * Test the singleton in a non-concurrent setting + */ + @Test + public void testMultipleCallsReturnTheSameObjectInSameThread() { + // Create several instances in the same calling thread + S instance1 = this.singletonInstanceMethod.get(); + S instance2 = this.singletonInstanceMethod.get(); + S instance3 = this.singletonInstanceMethod.get(); + // now check they are equal + assertSame(instance1, instance2); + assertSame(instance1, instance3); + assertSame(instance2, instance3); + } + + /** + * Test singleton instance in a concurrent setting + */ + @Test(timeout = 10000) + public void testMultipleCallsReturnTheSameObjectInDifferentThreads() throws Exception { + + // Create 10000 tasks and inside each callable instantiate the singleton class + final List> tasks = new ArrayList<>(); + for (int i = 0; i < 10000; i++) { + tasks.add(this.singletonInstanceMethod::get); + } + + // Use up to 8 concurrent threads to handle the tasks + final ExecutorService executorService = Executors.newFixedThreadPool(8); + final List> results = executorService.invokeAll(tasks); + + // wait for all of the threads to complete + final S expectedInstance = this.singletonInstanceMethod.get(); + for (Future res : results) { + final S instance = res.get(); + assertNotNull(instance); + assertSame(expectedInstance, instance); + } + + // tidy up the executor + executorService.shutdown(); + + } + +} diff --git a/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLockingTest.java b/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLockingTest.java new file mode 100644 index 000000000..61f29d9e8 --- /dev/null +++ b/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLockingTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.singleton; + +/** + * Date: 12/29/15 - 19:26 PM + * + * @author Jeroen Meulemeester + */ +public class ThreadSafeDoubleCheckLockingTest extends SingletonTest { + + /** + * Create a new singleton test instance using the given 'getInstance' method + */ + public ThreadSafeDoubleCheckLockingTest() { + super(ThreadSafeDoubleCheckLocking::getInstance); + } + +} \ No newline at end of file diff --git a/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTowerTest.java b/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTowerTest.java new file mode 100644 index 000000000..188749d1c --- /dev/null +++ b/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTowerTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.singleton; + +/** + * Date: 12/29/15 - 19:26 PM + * + * @author Jeroen Meulemeester + */ +public class ThreadSafeLazyLoadedIvoryTowerTest extends SingletonTest { + + /** + * Create a new singleton test instance using the given 'getInstance' method + */ + public ThreadSafeLazyLoadedIvoryTowerTest() { + super(ThreadSafeLazyLoadedIvoryTower::getInstance); + } + +} diff --git a/specification/README.md b/specification/README.md new file mode 100644 index 000000000..564830653 --- /dev/null +++ b/specification/README.md @@ -0,0 +1,35 @@ +--- +layout: pattern +title: Specification +folder: specification +permalink: /patterns/specification/ +categories: Behavioral +tags: + - Java + - Difficulty-Beginner +--- + +## Also known as +Filter, Criteria + +## Intent +Specification pattern separates the statement of how to match a +candidate, from the candidate object that it is matched against. As well as its +usefulness in selection, it is also valuable for validation and for building to +order + +![alt text](./etc/specification.png "Specification") + +## Applicability +Use the Specification pattern when + +* you need to select a subset of objects based on some criteria, and to refresh the selection at various times +* you need to check that only suitable objects are used for a certain role (validation) + +## Related patterns + +* Repository + +## Credits + +* [Martin Fowler - Specifications](http://martinfowler.com/apsupp/spec.pdf) diff --git a/specification/pom.xml b/specification/pom.xml index 533897e00..03c66540d 100644 --- a/specification/pom.xml +++ b/specification/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT specification @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/specification/src/main/java/com/iluwatar/specification/app/App.java b/specification/src/main/java/com/iluwatar/specification/app/App.java index 7a5f00a5c..7cbd38470 100644 --- a/specification/src/main/java/com/iluwatar/specification/app/App.java +++ b/specification/src/main/java/com/iluwatar/specification/app/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.specification.app; import java.util.Arrays; @@ -18,33 +40,44 @@ import com.iluwatar.specification.selector.MovementSelector; /** * - * The central idea of Specification pattern is to separate the statement of how to match a candidate, from the - * candidate object that it is matched against. As well as its usefulness in selection, it is also valuable for - * validation and for building to order. - * - * In this example we have a pool of creatures with different properties. We then have defined separate selection - * rules (Specifications) that we apply to the collection and as output receive only the creatures that match - * the selection criteria. - * + * The central idea of the Specification pattern is to separate the statement of how to match a + * candidate, from the candidate object that it is matched against. As well as its usefulness in + * selection, it is also valuable for validation and for building to order. + *

    + * In this example we have a pool of creatures with different properties. We then have defined + * separate selection rules (Specifications) that we apply to the collection and as output receive + * only the creatures that match the selection criteria. + *

    * http://martinfowler.com/apsupp/spec.pdf * */ public class App { - - public static void main( String[] args ) { - // initialize creatures list - List creatures = Arrays.asList(new Goblin(), new Octopus(), new Dragon(), new Shark(), new Troll(), new KillerBee()); - // find all walking creatures - System.out.println("Find all walking creatures"); - List walkingCreatures = creatures.stream().filter(new MovementSelector(Movement.WALKING)).collect(Collectors.toList()); - walkingCreatures.stream().forEach(System.out::println); - // find all dark creatures - System.out.println("Find all dark creatures"); - List darkCreatures = creatures.stream().filter(new ColorSelector(Color.DARK)).collect(Collectors.toList()); - darkCreatures.stream().forEach(System.out::println); - // find all red and flying creatures - System.out.println("Find all red and flying creatures"); - List redAndFlyingCreatures = creatures.stream().filter(new ColorSelector(Color.RED).and(new MovementSelector(Movement.FLYING))).collect(Collectors.toList()); - redAndFlyingCreatures.stream().forEach(System.out::println); - } + + /** + * Program entry point + */ + public static void main(String[] args) { + // initialize creatures list + List creatures = + Arrays.asList(new Goblin(), new Octopus(), new Dragon(), new Shark(), new Troll(), + new KillerBee()); + // find all walking creatures + System.out.println("Find all walking creatures"); + List walkingCreatures = + creatures.stream().filter(new MovementSelector(Movement.WALKING)) + .collect(Collectors.toList()); + walkingCreatures.stream().forEach(System.out::println); + // find all dark creatures + System.out.println("Find all dark creatures"); + List darkCreatures = + creatures.stream().filter(new ColorSelector(Color.DARK)).collect(Collectors.toList()); + darkCreatures.stream().forEach(System.out::println); + // find all red and flying creatures + System.out.println("Find all red and flying creatures"); + List redAndFlyingCreatures = + creatures.stream() + .filter(new ColorSelector(Color.RED).and(new MovementSelector(Movement.FLYING))) + .collect(Collectors.toList()); + redAndFlyingCreatures.stream().forEach(System.out::println); + } } diff --git a/specification/src/main/java/com/iluwatar/specification/creature/AbstractCreature.java b/specification/src/main/java/com/iluwatar/specification/creature/AbstractCreature.java index 12dae6cc9..8e88f13ae 100644 --- a/specification/src/main/java/com/iluwatar/specification/creature/AbstractCreature.java +++ b/specification/src/main/java/com/iluwatar/specification/creature/AbstractCreature.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.specification.creature; import com.iluwatar.specification.property.Color; @@ -11,40 +33,43 @@ import com.iluwatar.specification.property.Size; */ public abstract class AbstractCreature implements Creature { - private String name; - private Size size; - private Movement movement; - private Color color; + private String name; + private Size size; + private Movement movement; + private Color color; - public AbstractCreature(String name, Size size, Movement movement, Color color) { - this.name = name; - this.size = size; - this.movement = movement; - this.color = color; - } - - @Override - public String toString() { - return String.format("%s [size=%s, movement=%s, color=%s]", name, size, movement, color); - } - - @Override - public String getName() { - return name; - } - - @Override - public Size getSize() { - return size; - } - - @Override - public Movement getMovement() { - return movement; - } - - @Override - public Color getColor() { - return color; - } + /** + * Constructor + */ + public AbstractCreature(String name, Size size, Movement movement, Color color) { + this.name = name; + this.size = size; + this.movement = movement; + this.color = color; + } + + @Override + public String toString() { + return String.format("%s [size=%s, movement=%s, color=%s]", name, size, movement, color); + } + + @Override + public String getName() { + return name; + } + + @Override + public Size getSize() { + return size; + } + + @Override + public Movement getMovement() { + return movement; + } + + @Override + public Color getColor() { + return color; + } } diff --git a/specification/src/main/java/com/iluwatar/specification/creature/Creature.java b/specification/src/main/java/com/iluwatar/specification/creature/Creature.java index f2d1d38d7..a330af4e7 100644 --- a/specification/src/main/java/com/iluwatar/specification/creature/Creature.java +++ b/specification/src/main/java/com/iluwatar/specification/creature/Creature.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.specification.creature; import com.iluwatar.specification.property.Color; @@ -11,11 +33,11 @@ import com.iluwatar.specification.property.Size; */ public interface Creature { - String getName(); - - Size getSize(); - - Movement getMovement(); - - Color getColor(); + String getName(); + + Size getSize(); + + Movement getMovement(); + + Color getColor(); } diff --git a/specification/src/main/java/com/iluwatar/specification/creature/Dragon.java b/specification/src/main/java/com/iluwatar/specification/creature/Dragon.java index 0a6fd31ba..ab07001f7 100644 --- a/specification/src/main/java/com/iluwatar/specification/creature/Dragon.java +++ b/specification/src/main/java/com/iluwatar/specification/creature/Dragon.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.specification.creature; import com.iluwatar.specification.property.Color; @@ -11,7 +33,7 @@ import com.iluwatar.specification.property.Size; */ public class Dragon extends AbstractCreature { - public Dragon() { - super("Dragon", Size.LARGE, Movement.FLYING, Color.RED); - } + public Dragon() { + super("Dragon", Size.LARGE, Movement.FLYING, Color.RED); + } } diff --git a/specification/src/main/java/com/iluwatar/specification/creature/Goblin.java b/specification/src/main/java/com/iluwatar/specification/creature/Goblin.java index f7cc1ef0b..1b53a7e77 100644 --- a/specification/src/main/java/com/iluwatar/specification/creature/Goblin.java +++ b/specification/src/main/java/com/iluwatar/specification/creature/Goblin.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.specification.creature; import com.iluwatar.specification.property.Color; @@ -11,7 +33,7 @@ import com.iluwatar.specification.property.Size; */ public class Goblin extends AbstractCreature { - public Goblin() { - super("Goblin", Size.SMALL, Movement.WALKING, Color.GREEN); - } + public Goblin() { + super("Goblin", Size.SMALL, Movement.WALKING, Color.GREEN); + } } diff --git a/specification/src/main/java/com/iluwatar/specification/creature/KillerBee.java b/specification/src/main/java/com/iluwatar/specification/creature/KillerBee.java index 11a4711c7..4c98e9041 100644 --- a/specification/src/main/java/com/iluwatar/specification/creature/KillerBee.java +++ b/specification/src/main/java/com/iluwatar/specification/creature/KillerBee.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.specification.creature; import com.iluwatar.specification.property.Color; @@ -11,7 +33,7 @@ import com.iluwatar.specification.property.Size; */ public class KillerBee extends AbstractCreature { - public KillerBee() { - super("KillerBee", Size.SMALL, Movement.FLYING, Color.LIGHT); - } + public KillerBee() { + super("KillerBee", Size.SMALL, Movement.FLYING, Color.LIGHT); + } } diff --git a/specification/src/main/java/com/iluwatar/specification/creature/Octopus.java b/specification/src/main/java/com/iluwatar/specification/creature/Octopus.java index 7a2ae2c18..d74ba357f 100644 --- a/specification/src/main/java/com/iluwatar/specification/creature/Octopus.java +++ b/specification/src/main/java/com/iluwatar/specification/creature/Octopus.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.specification.creature; import com.iluwatar.specification.property.Color; @@ -11,7 +33,7 @@ import com.iluwatar.specification.property.Size; */ public class Octopus extends AbstractCreature { - public Octopus() { - super("Octopus", Size.NORMAL, Movement.SWIMMING, Color.DARK); - } + public Octopus() { + super("Octopus", Size.NORMAL, Movement.SWIMMING, Color.DARK); + } } diff --git a/specification/src/main/java/com/iluwatar/specification/creature/Shark.java b/specification/src/main/java/com/iluwatar/specification/creature/Shark.java index 42090500d..69f4c9b38 100644 --- a/specification/src/main/java/com/iluwatar/specification/creature/Shark.java +++ b/specification/src/main/java/com/iluwatar/specification/creature/Shark.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.specification.creature; import com.iluwatar.specification.property.Color; @@ -11,7 +33,7 @@ import com.iluwatar.specification.property.Size; */ public class Shark extends AbstractCreature { - public Shark() { - super("Shark", Size.NORMAL, Movement.SWIMMING, Color.LIGHT); - } + public Shark() { + super("Shark", Size.NORMAL, Movement.SWIMMING, Color.LIGHT); + } } diff --git a/specification/src/main/java/com/iluwatar/specification/creature/Troll.java b/specification/src/main/java/com/iluwatar/specification/creature/Troll.java index 1dd31c17f..f480a0723 100644 --- a/specification/src/main/java/com/iluwatar/specification/creature/Troll.java +++ b/specification/src/main/java/com/iluwatar/specification/creature/Troll.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.specification.creature; import com.iluwatar.specification.property.Color; @@ -10,8 +32,8 @@ import com.iluwatar.specification.property.Size; * */ public class Troll extends AbstractCreature { - - public Troll() { - super("Troll", Size.LARGE, Movement.WALKING, Color.DARK); - } + + public Troll() { + super("Troll", Size.LARGE, Movement.WALKING, Color.DARK); + } } diff --git a/specification/src/main/java/com/iluwatar/specification/property/Color.java b/specification/src/main/java/com/iluwatar/specification/property/Color.java index e23cb1585..583602990 100644 --- a/specification/src/main/java/com/iluwatar/specification/property/Color.java +++ b/specification/src/main/java/com/iluwatar/specification/property/Color.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.specification.property; /** @@ -7,16 +29,16 @@ package com.iluwatar.specification.property; */ public enum Color { - DARK("dark"), LIGHT("light"), GREEN("green"), RED("red"); - - private String title; + DARK("dark"), LIGHT("light"), GREEN("green"), RED("red"); - Color(String title) { - this.title = title; - } + private String title; - @Override - public String toString() { - return title; - } + Color(String title) { + this.title = title; + } + + @Override + public String toString() { + return title; + } } diff --git a/specification/src/main/java/com/iluwatar/specification/property/Movement.java b/specification/src/main/java/com/iluwatar/specification/property/Movement.java index 1b4b0ec9e..ae26d1a30 100644 --- a/specification/src/main/java/com/iluwatar/specification/property/Movement.java +++ b/specification/src/main/java/com/iluwatar/specification/property/Movement.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.specification.property; /** @@ -7,16 +29,16 @@ package com.iluwatar.specification.property; */ public enum Movement { - WALKING("walking"), SWIMMING("swimming"), FLYING("flying"); - - private String title; + WALKING("walking"), SWIMMING("swimming"), FLYING("flying"); - Movement(String title) { - this.title = title; - } + private String title; - @Override - public String toString() { - return title; - } + Movement(String title) { + this.title = title; + } + + @Override + public String toString() { + return title; + } } diff --git a/specification/src/main/java/com/iluwatar/specification/property/Size.java b/specification/src/main/java/com/iluwatar/specification/property/Size.java index 7f07fe147..239caa586 100644 --- a/specification/src/main/java/com/iluwatar/specification/property/Size.java +++ b/specification/src/main/java/com/iluwatar/specification/property/Size.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.specification.property; /** @@ -7,16 +29,16 @@ package com.iluwatar.specification.property; */ public enum Size { - SMALL("small"), NORMAL("normal"), LARGE("large"); - - private String title; + SMALL("small"), NORMAL("normal"), LARGE("large"); - Size(String title) { - this.title = title; - } + private String title; - @Override - public String toString() { - return title; - } + Size(String title) { + this.title = title; + } + + @Override + public String toString() { + return title; + } } diff --git a/specification/src/main/java/com/iluwatar/specification/selector/ColorSelector.java b/specification/src/main/java/com/iluwatar/specification/selector/ColorSelector.java index d68127599..c1c178e23 100644 --- a/specification/src/main/java/com/iluwatar/specification/selector/ColorSelector.java +++ b/specification/src/main/java/com/iluwatar/specification/selector/ColorSelector.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.specification.selector; import java.util.function.Predicate; @@ -12,14 +34,14 @@ import com.iluwatar.specification.property.Color; */ public class ColorSelector implements Predicate { - private final Color c; + private final Color c; - public ColorSelector(Color c) { - this.c = c; - } - - @Override - public boolean test(Creature t) { - return t.getColor().equals(c); - } + public ColorSelector(Color c) { + this.c = c; + } + + @Override + public boolean test(Creature t) { + return t.getColor().equals(c); + } } diff --git a/specification/src/main/java/com/iluwatar/specification/selector/MovementSelector.java b/specification/src/main/java/com/iluwatar/specification/selector/MovementSelector.java index 260abd0e3..67d7abd61 100644 --- a/specification/src/main/java/com/iluwatar/specification/selector/MovementSelector.java +++ b/specification/src/main/java/com/iluwatar/specification/selector/MovementSelector.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.specification.selector; import java.util.function.Predicate; @@ -11,15 +33,15 @@ import com.iluwatar.specification.property.Movement; * */ public class MovementSelector implements Predicate { - - private final Movement m; - public MovementSelector(Movement m) { - this.m = m; - } + private final Movement m; - @Override - public boolean test(Creature t) { - return t.getMovement().equals(m); - } + public MovementSelector(Movement m) { + this.m = m; + } + + @Override + public boolean test(Creature t) { + return t.getMovement().equals(m); + } } diff --git a/specification/src/main/java/com/iluwatar/specification/selector/SizeSelector.java b/specification/src/main/java/com/iluwatar/specification/selector/SizeSelector.java index a54eaf16c..2792531d0 100644 --- a/specification/src/main/java/com/iluwatar/specification/selector/SizeSelector.java +++ b/specification/src/main/java/com/iluwatar/specification/selector/SizeSelector.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.specification.selector; import java.util.function.Predicate; @@ -12,14 +34,14 @@ import com.iluwatar.specification.property.Size; */ public class SizeSelector implements Predicate { - private final Size s; + private final Size s; - public SizeSelector(Size s) { - this.s = s; - } - - @Override - public boolean test(Creature t) { - return t.getSize().equals(s); - } + public SizeSelector(Size s) { + this.s = s; + } + + @Override + public boolean test(Creature t) { + return t.getSize().equals(s); + } } diff --git a/specification/src/test/java/com/iluwatar/specification/app/AppTest.java b/specification/src/test/java/com/iluwatar/specification/app/AppTest.java index 8e27167dc..13e6c9b5d 100644 --- a/specification/src/test/java/com/iluwatar/specification/app/AppTest.java +++ b/specification/src/test/java/com/iluwatar/specification/app/AppTest.java @@ -1,14 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.specification.app; import org.junit.Test; -import com.iluwatar.specification.app.App; - +/** + * + * Application test + * + */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/specification/src/test/java/com/iluwatar/specification/creature/CreatureTest.java b/specification/src/test/java/com/iluwatar/specification/creature/CreatureTest.java new file mode 100644 index 000000000..22b27c8a0 --- /dev/null +++ b/specification/src/test/java/com/iluwatar/specification/creature/CreatureTest.java @@ -0,0 +1,133 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.specification.creature; + +import com.iluwatar.specification.property.Color; +import com.iluwatar.specification.property.Movement; +import com.iluwatar.specification.property.Size; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; +import java.util.Collection; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/29/15 - 7:47 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class CreatureTest { + + /** + * @return The tested {@link Creature} instance and its expected specs + */ + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList( + new Object[]{new Dragon(), "Dragon", Size.LARGE, Movement.FLYING, Color.RED}, + new Object[]{new Goblin(), "Goblin", Size.SMALL, Movement.WALKING, Color.GREEN}, + new Object[]{new KillerBee(), "KillerBee", Size.SMALL, Movement.FLYING, Color.LIGHT}, + new Object[]{new Octopus(), "Octopus", Size.NORMAL, Movement.SWIMMING, Color.DARK}, + new Object[]{new Shark(), "Shark", Size.NORMAL, Movement.SWIMMING, Color.LIGHT}, + new Object[]{new Troll(), "Troll", Size.LARGE, Movement.WALKING, Color.DARK} + ); + } + + /** + * The tested creature + */ + private final Creature testedCreature; + + /** + * The expected name of the tested creature + */ + private final String name; + + /** + * The expected size of the tested creature + */ + private final Size size; + + /** + * The expected movement type of the tested creature + */ + private final Movement movement; + + /** + * The expected color of the tested creature + */ + private final Color color; + + /** + * @param testedCreature The tested creature + * @param name The expected name of the creature + * @param size The expected size of the creature + * @param movement The expected movement type of the creature + * @param color The expected color of the creature + */ + public CreatureTest(final Creature testedCreature, final String name, final Size size, + final Movement movement, final Color color) { + this.testedCreature = testedCreature; + this.name = name; + this.size = size; + this.movement = movement; + this.color = color; + } + + + @Test + public void testGetName() throws Exception { + assertEquals(this.name, this.testedCreature.getName()); + } + + @Test + public void testGetSize() throws Exception { + assertEquals(this.size, this.testedCreature.getSize()); + } + + @Test + public void testGetMovement() throws Exception { + assertEquals(this.movement, this.testedCreature.getMovement()); + } + + @Test + public void testGetColor() throws Exception { + assertEquals(this.color, this.testedCreature.getColor()); + } + + @Test + public void testToString() throws Exception { + final String toString = this.testedCreature.toString(); + assertNotNull(toString); + assertEquals( + String.format("%s [size=%s, movement=%s, color=%s]", name, size, movement, color), + toString + ); + } +} \ No newline at end of file diff --git a/specification/src/test/java/com/iluwatar/specification/selector/ColorSelectorTest.java b/specification/src/test/java/com/iluwatar/specification/selector/ColorSelectorTest.java new file mode 100644 index 000000000..0d6dfa080 --- /dev/null +++ b/specification/src/test/java/com/iluwatar/specification/selector/ColorSelectorTest.java @@ -0,0 +1,59 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.specification.selector; + +import com.iluwatar.specification.creature.Creature; +import com.iluwatar.specification.property.Color; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Date: 12/29/15 - 7:35 PM + * + * @author Jeroen Meulemeester + */ +public class ColorSelectorTest { + + /** + * Verify if the color selector gives the correct results + */ + @Test + public void testColor() { + final Creature greenCreature = mock(Creature.class); + when(greenCreature.getColor()).thenReturn(Color.GREEN); + + final Creature redCreature = mock(Creature.class); + when(redCreature.getColor()).thenReturn(Color.RED); + + final ColorSelector greenSelector = new ColorSelector(Color.GREEN); + assertTrue(greenSelector.test(greenCreature)); + assertFalse(greenSelector.test(redCreature)); + + } + +} \ No newline at end of file diff --git a/specification/src/test/java/com/iluwatar/specification/selector/MovementSelectorTest.java b/specification/src/test/java/com/iluwatar/specification/selector/MovementSelectorTest.java new file mode 100644 index 000000000..451c776dd --- /dev/null +++ b/specification/src/test/java/com/iluwatar/specification/selector/MovementSelectorTest.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.specification.selector; + +import com.iluwatar.specification.creature.Creature; +import com.iluwatar.specification.property.Color; +import com.iluwatar.specification.property.Movement; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Date: 12/29/15 - 7:37 PM + * + * @author Jeroen Meulemeester + */ +public class MovementSelectorTest { + + /** + * Verify if the movement selector gives the correct results + */ + @Test + public void testMovement() { + final Creature swimmingCreature = mock(Creature.class); + when(swimmingCreature.getMovement()).thenReturn(Movement.SWIMMING); + + final Creature flyingCreature = mock(Creature.class); + when(flyingCreature.getMovement()).thenReturn(Movement.FLYING); + + final MovementSelector swimmingSelector = new MovementSelector(Movement.SWIMMING); + assertTrue(swimmingSelector.test(swimmingCreature)); + assertFalse(swimmingSelector.test(flyingCreature)); + + } + +} \ No newline at end of file diff --git a/specification/src/test/java/com/iluwatar/specification/selector/SizeSelectorTest.java b/specification/src/test/java/com/iluwatar/specification/selector/SizeSelectorTest.java new file mode 100644 index 000000000..de1228f1b --- /dev/null +++ b/specification/src/test/java/com/iluwatar/specification/selector/SizeSelectorTest.java @@ -0,0 +1,58 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.specification.selector; + +import com.iluwatar.specification.creature.Creature; +import com.iluwatar.specification.property.Size; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Date: 12/29/15 - 7:43 PM + * + * @author Jeroen Meulemeester + */ +public class SizeSelectorTest { + + /** + * Verify if the size selector gives the correct results + */ + @Test + public void testMovement() { + final Creature normalCreature = mock(Creature.class); + when(normalCreature.getSize()).thenReturn(Size.NORMAL); + + final Creature smallCreature = mock(Creature.class); + when(smallCreature.getSize()).thenReturn(Size.SMALL); + + final SizeSelector normalSelector = new SizeSelector(Size.NORMAL); + assertTrue(normalSelector.test(normalCreature)); + assertFalse(normalSelector.test(smallCreature)); + } + +} diff --git a/state/README.md b/state/README.md new file mode 100644 index 000000000..f5cb189fd --- /dev/null +++ b/state/README.md @@ -0,0 +1,30 @@ +--- +layout: pattern +title: State +folder: state +permalink: /patterns/state/ +categories: Behavioral +tags: + - Java + - Difficulty-Intermediate + - Gang Of Four +--- + +## Also known as +Objects for States + +## Intent +Allow an object to alter its behavior when its internal state +changes. The object will appear to change its class. + +![alt text](./etc/state_1.png "State") + +## Applicability +Use the State pattern in either of the following cases + +* an object's behavior depends on its state, and it must change its behavior at run-time depending on that state +* operations have large, multipart conditional statements that depend on the object's state. This state is usually represented by one or more enumerated constants. Often, several operations will contain this same conditional structure. The State pattern puts each branch of the conditional in a separate class. This lets you treat the object's state as an object in its own right that can vary independently from other objects. + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/state/pom.xml b/state/pom.xml index 7f8bb7fbf..134fbabe1 100644 --- a/state/pom.xml +++ b/state/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT state @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/state/src/main/java/com/iluwatar/state/AngryState.java b/state/src/main/java/com/iluwatar/state/AngryState.java index f09fbb518..c58f85ae1 100644 --- a/state/src/main/java/com/iluwatar/state/AngryState.java +++ b/state/src/main/java/com/iluwatar/state/AngryState.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.state; /** @@ -7,20 +29,20 @@ package com.iluwatar.state; */ public class AngryState implements State { - private Mammoth mammoth; + private Mammoth mammoth; - public AngryState(Mammoth mammoth) { - this.mammoth = mammoth; - } + public AngryState(Mammoth mammoth) { + this.mammoth = mammoth; + } - @Override - public void observe() { - System.out.println(String.format("%s is furious!", mammoth)); - } + @Override + public void observe() { + System.out.println(String.format("%s is furious!", mammoth)); + } - @Override - public void onEnterState() { - System.out.println(String.format("%s gets angry!", mammoth)); - } + @Override + public void onEnterState() { + System.out.println(String.format("%s gets angry!", mammoth)); + } } diff --git a/state/src/main/java/com/iluwatar/state/App.java b/state/src/main/java/com/iluwatar/state/App.java index eefa8f766..9f9d8d29a 100644 --- a/state/src/main/java/com/iluwatar/state/App.java +++ b/state/src/main/java/com/iluwatar/state/App.java @@ -1,24 +1,51 @@ -package com.iluwatar.state; - -/** - * - * In State pattern the container object (Mammoth) has an internal state object (State) that - * defines the current behavior. The state object can be changed to alter the - * behavior. - * - * In this example the mammoth changes its behavior as time passes by. - * - */ -public class App { - - public static void main(String[] args) { - - Mammoth mammoth = new Mammoth(); - mammoth.observe(); - mammoth.timePasses(); - mammoth.observe(); - mammoth.timePasses(); - mammoth.observe(); - - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.state; + +/** + * + * In State pattern the container object has an internal state object that defines the current + * behavior. The state object can be changed to alter the behavior. + *

    + * This can be a cleaner way for an object to change its behavior at runtime without resorting to + * large monolithic conditional statements and thus improves maintainability. + *

    + * In this example the {@link Mammoth} changes its behavior as time passes by. + * + */ +public class App { + + /** + * Program entry point + */ + public static void main(String[] args) { + + Mammoth mammoth = new Mammoth(); + mammoth.observe(); + mammoth.timePasses(); + mammoth.observe(); + mammoth.timePasses(); + mammoth.observe(); + + } +} diff --git a/state/src/main/java/com/iluwatar/state/Mammoth.java b/state/src/main/java/com/iluwatar/state/Mammoth.java index f98da3b5e..ffa07ed68 100644 --- a/state/src/main/java/com/iluwatar/state/Mammoth.java +++ b/state/src/main/java/com/iluwatar/state/Mammoth.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.state; /** @@ -7,31 +29,34 @@ package com.iluwatar.state; */ public class Mammoth { - private State state; + private State state; - public Mammoth() { - state = new PeacefulState(this); - } + public Mammoth() { + state = new PeacefulState(this); + } - public void timePasses() { - if (state.getClass().equals(PeacefulState.class)) { - changeStateTo(new AngryState(this)); - } else { - changeStateTo(new PeacefulState(this)); - } - } + /** + * Makes time pass for the mammoth + */ + public void timePasses() { + if (state.getClass().equals(PeacefulState.class)) { + changeStateTo(new AngryState(this)); + } else { + changeStateTo(new PeacefulState(this)); + } + } - private void changeStateTo(State newState) { - this.state = newState; - this.state.onEnterState(); - } + private void changeStateTo(State newState) { + this.state = newState; + this.state.onEnterState(); + } - @Override - public String toString() { - return "The mammoth"; - } + @Override + public String toString() { + return "The mammoth"; + } - public void observe() { - this.state.observe(); - } + public void observe() { + this.state.observe(); + } } diff --git a/state/src/main/java/com/iluwatar/state/PeacefulState.java b/state/src/main/java/com/iluwatar/state/PeacefulState.java index 237fe7c2c..23f4e893c 100644 --- a/state/src/main/java/com/iluwatar/state/PeacefulState.java +++ b/state/src/main/java/com/iluwatar/state/PeacefulState.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.state; /** @@ -7,20 +29,20 @@ package com.iluwatar.state; */ public class PeacefulState implements State { - private Mammoth mammoth; + private Mammoth mammoth; - public PeacefulState(Mammoth mammoth) { - this.mammoth = mammoth; - } + public PeacefulState(Mammoth mammoth) { + this.mammoth = mammoth; + } - @Override - public void observe() { - System.out.println(String.format("%s is calm and peaceful.", mammoth)); - } + @Override + public void observe() { + System.out.println(String.format("%s is calm and peaceful.", mammoth)); + } - @Override - public void onEnterState() { - System.out.println(String.format("%s calms down.", mammoth)); - } + @Override + public void onEnterState() { + System.out.println(String.format("%s calms down.", mammoth)); + } } diff --git a/state/src/main/java/com/iluwatar/state/State.java b/state/src/main/java/com/iluwatar/state/State.java index 3f65e3532..65c59af16 100644 --- a/state/src/main/java/com/iluwatar/state/State.java +++ b/state/src/main/java/com/iluwatar/state/State.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.state; /** @@ -7,8 +29,8 @@ package com.iluwatar.state; */ public interface State { - void onEnterState(); + void onEnterState(); - void observe(); + void observe(); } diff --git a/state/src/test/java/com/iluwatar/state/AppTest.java b/state/src/test/java/com/iluwatar/state/AppTest.java index fae8412fc..9dc3790c7 100644 --- a/state/src/test/java/com/iluwatar/state/AppTest.java +++ b/state/src/test/java/com/iluwatar/state/AppTest.java @@ -1,14 +1,39 @@ -package com.iluwatar.state; - -import org.junit.Test; - -import com.iluwatar.state.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.state; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/state/src/test/java/com/iluwatar/state/MammothTest.java b/state/src/test/java/com/iluwatar/state/MammothTest.java new file mode 100644 index 000000000..4fe37bfd1 --- /dev/null +++ b/state/src/test/java/com/iluwatar/state/MammothTest.java @@ -0,0 +1,112 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.state; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mockito; + +import java.io.PrintStream; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; + +/** + * Date: 12/29/15 - 8:27 PM + * + * @author Jeroen Meulemeester + */ +public class MammothTest { + + /** + * The mocked standard out {@link PrintStream}, required since some actions don't have any + * influence on accessible objects, except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Switch to a complete mammoth 'mood'-cycle and verify if the observed mood matches the expected + * value. + */ + @Test + public void testTimePasses() { + final InOrder inOrder = Mockito.inOrder(this.stdOutMock); + final Mammoth mammoth = new Mammoth(); + + mammoth.observe(); + inOrder.verify(this.stdOutMock).println("The mammoth is calm and peaceful."); + inOrder.verifyNoMoreInteractions(); + + mammoth.timePasses(); + inOrder.verify(this.stdOutMock).println("The mammoth gets angry!"); + inOrder.verifyNoMoreInteractions(); + + mammoth.observe(); + inOrder.verify(this.stdOutMock).println("The mammoth is furious!"); + inOrder.verifyNoMoreInteractions(); + + mammoth.timePasses(); + inOrder.verify(this.stdOutMock).println("The mammoth calms down."); + inOrder.verifyNoMoreInteractions(); + + mammoth.observe(); + inOrder.verify(this.stdOutMock).println("The mammoth is calm and peaceful."); + inOrder.verifyNoMoreInteractions(); + + } + + /** + * Verify if {@link Mammoth#toString()} gives the expected value + */ + @Test + public void testToString() { + final String toString = new Mammoth().toString(); + assertNotNull(toString); + assertEquals("The mammoth", toString); + } + +} \ No newline at end of file diff --git a/step-builder/README.md b/step-builder/README.md new file mode 100644 index 000000000..bc636e37a --- /dev/null +++ b/step-builder/README.md @@ -0,0 +1,23 @@ +--- +layout: pattern +title: Step Builder +folder: step-builder +permalink: /patterns/step-builder/ +categories: Creational +tags: + - Java + - Difficulty-Intermediate +--- + +## Intent +An extension of the Builder pattern that fully guides the user through the creation of the object with no chances of confusion. +The user experience will be much more improved by the fact that he will only see the next step methods available, NO build method until is the right time to build the object. + +![alt text](./etc/step-builder.png "Step Builder") + +## Applicability +Use the Step Builder pattern when the algorithm for creating a complex object should be independent of the parts that make up the object and how they're assembled the construction process must allow different representations for the object that's constructed when in the process of constructing the order is important. + +## Credits + +* [Marco Castigliego - Step Builder](http://rdafbn.blogspot.co.uk/2012/07/step-builder-pattern_28.html) diff --git a/step-builder/etc/step-builder.png b/step-builder/etc/step-builder.png new file mode 100644 index 000000000..b7b623657 Binary files /dev/null and b/step-builder/etc/step-builder.png differ diff --git a/step-builder/etc/step-builder.ucls b/step-builder/etc/step-builder.ucls new file mode 100644 index 000000000..bb3560a2e --- /dev/null +++ b/step-builder/etc/step-builder.ucls @@ -0,0 +1,181 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/step-builder/pom.xml b/step-builder/pom.xml new file mode 100644 index 000000000..96098eabc --- /dev/null +++ b/step-builder/pom.xml @@ -0,0 +1,43 @@ + + + + 4.0.0 + + java-design-patterns + com.iluwatar + 1.13.0-SNAPSHOT + + step-builder + + + junit + junit + test + + + \ No newline at end of file diff --git a/step-builder/src/main/java/com/iluwatar/stepbuilder/App.java b/step-builder/src/main/java/com/iluwatar/stepbuilder/App.java new file mode 100644 index 000000000..aeb759ba8 --- /dev/null +++ b/step-builder/src/main/java/com/iluwatar/stepbuilder/App.java @@ -0,0 +1,84 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.stepbuilder; + +/** + * Step Builder Pattern + * + *

    + * Intent
    + * An extension of the Builder pattern that fully guides the user through the creation of the object + * with no chances of confusion.
    + * The user experience will be much more improved by the fact that he will only see the next step + * methods available, NO build method until is the right time to build the object. + * + *

    + * Implementation + *

      + * The concept is simple: + * + *
    • Write creational steps inner classes or interfaces where each method knows what can be + * displayed next.
    • + * + *
    • Implement all your steps interfaces in an inner static class.
    • + * + *
    • Last step is the BuildStep, in charge of creating the object you need to build.
    • + *
    + * + *

    + * Applicability
    + * Use the Step Builder pattern when the algorithm for creating a complex object should be + * independent of the parts that make up the object and how they're assembled the construction + * process must allow different representations for the object that's constructed when in the + * process of constructing the order is important. + *

    + * http://rdafbn.blogspot.co.uk/2012/07/step-builder-pattern_28.html + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + + Character warrior = + CharacterStepBuilder.newBuilder().name("Amberjill").fighterClass("Paladin") + .withWeapon("Sword").noAbilities().build(); + + System.out.println(warrior); + + Character mage = + CharacterStepBuilder.newBuilder().name("Riobard").wizardClass("Sorcerer") + .withSpell("Fireball").withAbility("Fire Aura").withAbility("Teleport") + .noMoreAbilities().build(); + + System.out.println(mage); + + Character thief = + CharacterStepBuilder.newBuilder().name("Desmond").fighterClass("Rogue").noWeapon().build(); + + System.out.println(thief); + } +} diff --git a/step-builder/src/main/java/com/iluwatar/stepbuilder/Character.java b/step-builder/src/main/java/com/iluwatar/stepbuilder/Character.java new file mode 100644 index 000000000..1e21758a3 --- /dev/null +++ b/step-builder/src/main/java/com/iluwatar/stepbuilder/Character.java @@ -0,0 +1,104 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.stepbuilder; + +import java.util.List; + +/** + * The class with many parameters. + */ +public class Character { + + private String name; + private String fighterClass; + private String wizardClass; + private String weapon; + private String spell; + private List abilities; + + public Character(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getFighterClass() { + return fighterClass; + } + + public void setFighterClass(String fighterClass) { + this.fighterClass = fighterClass; + } + + public String getWizardClass() { + return wizardClass; + } + + public void setWizardClass(String wizardClass) { + this.wizardClass = wizardClass; + } + + public String getWeapon() { + return weapon; + } + + public void setWeapon(String weapon) { + this.weapon = weapon; + } + + public String getSpell() { + return spell; + } + + public void setSpell(String spell) { + this.spell = spell; + } + + public List getAbilities() { + return abilities; + } + + public void setAbilities(List abilities) { + this.abilities = abilities; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("This is a ") + .append(fighterClass != null ? fighterClass : wizardClass) + .append(" named ") + .append(name) + .append(" armed with a ") + .append(weapon != null ? weapon : spell != null ? spell : "with nothing") + .append(abilities != null ? " and wielding " + abilities + " abilities" : "") + .append('.'); + return sb.toString(); + } +} diff --git a/step-builder/src/main/java/com/iluwatar/stepbuilder/CharacterStepBuilder.java b/step-builder/src/main/java/com/iluwatar/stepbuilder/CharacterStepBuilder.java new file mode 100644 index 000000000..35e671b4c --- /dev/null +++ b/step-builder/src/main/java/com/iluwatar/stepbuilder/CharacterStepBuilder.java @@ -0,0 +1,187 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.stepbuilder; + +import java.util.ArrayList; +import java.util.List; + +/** + * The Step Builder class. + */ +public final class CharacterStepBuilder { + + private CharacterStepBuilder() {} + + public static NameStep newBuilder() { + return new CharacterSteps(); + } + + /** + * First Builder Step in charge of the Character name. Next Step available : ClassStep + */ + public interface NameStep { + ClassStep name(String name); + } + + /** + * This step is in charge of setting the Character class (fighter or wizard). Fighter choice : + * Next Step available : WeaponStep Wizard choice : Next Step available : SpellStep + */ + public interface ClassStep { + WeaponStep fighterClass(String fighterClass); + + SpellStep wizardClass(String wizardClass); + } + + /** + * This step is in charge of the weapon. Weapon choice : Next Step available : AbilityStep No + * weapon choice : Next Step available : BuildStep + */ + public interface WeaponStep { + AbilityStep withWeapon(String weapon); + + BuildStep noWeapon(); + } + + /** + * This step is in charge of the spell. Spell choice : Next Step available : AbilityStep No spell + * choice : Next Step available : BuildStep + */ + public interface SpellStep { + AbilityStep withSpell(String spell); + + BuildStep noSpell(); + } + + /** + * This step is in charge of abilities. Next Step available : BuildStep + */ + public interface AbilityStep { + AbilityStep withAbility(String ability); + + BuildStep noMoreAbilities(); + + BuildStep noAbilities(); + } + + /** + * This is the final step in charge of building the Character Object. Validation should be here. + */ + public interface BuildStep { + Character build(); + } + + + /** + * Step Builder implementation. + */ + private static class CharacterSteps implements NameStep, ClassStep, WeaponStep, SpellStep, + AbilityStep, BuildStep { + + private String name; + private String fighterClass; + private String wizardClass; + private String weapon; + private String spell; + private List abilities = new ArrayList<>(); + + @Override + public ClassStep name(String name) { + this.name = name; + return this; + } + + @Override + public WeaponStep fighterClass(String fighterClass) { + this.fighterClass = fighterClass; + return this; + } + + @Override + public SpellStep wizardClass(String wizardClass) { + this.wizardClass = wizardClass; + return this; + } + + @Override + public AbilityStep withWeapon(String weapon) { + this.weapon = weapon; + return this; + } + + @Override + public BuildStep noWeapon() { + return this; + } + + @Override + public AbilityStep withSpell(String spell) { + this.spell = spell; + return this; + } + + @Override + public BuildStep noSpell() { + return this; + } + + @Override + public AbilityStep withAbility(String ability) { + this.abilities.add(ability); + return this; + } + + @Override + public BuildStep noMoreAbilities() { + return this; + } + + @Override + public BuildStep noAbilities() { + return this; + } + + @Override + public Character build() { + Character character = new Character(name); + + if (fighterClass != null) { + character.setFighterClass(fighterClass); + } else { + character.setWizardClass(wizardClass); + } + + if (weapon != null) { + character.setWeapon(weapon); + } else { + character.setSpell(spell); + } + + if (!abilities.isEmpty()) { + character.setAbilities(abilities); + } + + return character; + } + } +} diff --git a/step-builder/src/test/java/com/iluwatar/stepbuilder/AppTest.java b/step-builder/src/test/java/com/iluwatar/stepbuilder/AppTest.java new file mode 100644 index 000000000..41511d913 --- /dev/null +++ b/step-builder/src/test/java/com/iluwatar/stepbuilder/AppTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.stepbuilder; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/step-builder/src/test/java/com/iluwatar/stepbuilder/CharacterStepBuilderTest.java b/step-builder/src/test/java/com/iluwatar/stepbuilder/CharacterStepBuilderTest.java new file mode 100644 index 000000000..baa8f9d1d --- /dev/null +++ b/step-builder/src/test/java/com/iluwatar/stepbuilder/CharacterStepBuilderTest.java @@ -0,0 +1,177 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.stepbuilder; + +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +/** + * Date: 12/29/15 - 9:21 PM + * + * @author Jeroen Meulemeester + */ +public class CharacterStepBuilderTest { + + /** + * Build a new wizard {@link Character} and verify if it has the expected attributes + */ + @Test + public void testBuildWizard() { + final Character character = CharacterStepBuilder.newBuilder() + .name("Merlin") + .wizardClass("alchemist") + .withSpell("poison") + .withAbility("invisibility") + .withAbility("wisdom") + .noMoreAbilities() + .build(); + + assertEquals("Merlin", character.getName()); + assertEquals("alchemist", character.getWizardClass()); + assertEquals("poison", character.getSpell()); + assertNotNull(character.toString()); + + final List abilities = character.getAbilities(); + assertNotNull(abilities); + assertEquals(2, abilities.size()); + assertTrue(abilities.contains("invisibility")); + assertTrue(abilities.contains("wisdom")); + + } + + /** + * Build a new wizard {@link Character} without spell or abilities and verify if it has the + * expected attributes + */ + @Test + public void testBuildPoorWizard() { + final Character character = CharacterStepBuilder.newBuilder() + .name("Merlin") + .wizardClass("alchemist") + .noSpell() + .build(); + + assertEquals("Merlin", character.getName()); + assertEquals("alchemist", character.getWizardClass()); + assertNull(character.getSpell()); + assertNull(character.getAbilities()); + assertNotNull(character.toString()); + + } + + /** + * Build a new wizard {@link Character} and verify if it has the expected attributes + */ + @Test + public void testBuildWeakWizard() { + final Character character = CharacterStepBuilder.newBuilder() + .name("Merlin") + .wizardClass("alchemist") + .withSpell("poison") + .noAbilities() + .build(); + + assertEquals("Merlin", character.getName()); + assertEquals("alchemist", character.getWizardClass()); + assertEquals("poison", character.getSpell()); + assertNull(character.getAbilities()); + assertNotNull(character.toString()); + + } + + + /** + * Build a new warrior {@link Character} and verify if it has the expected attributes + */ + @Test + public void testBuildWarrior() { + final Character character = CharacterStepBuilder.newBuilder() + .name("Cuauhtemoc") + .fighterClass("aztec") + .withWeapon("spear") + .withAbility("speed") + .withAbility("strength") + .noMoreAbilities() + .build(); + + assertEquals("Cuauhtemoc", character.getName()); + assertEquals("aztec", character.getFighterClass()); + assertEquals("spear", character.getWeapon()); + assertNotNull(character.toString()); + + final List abilities = character.getAbilities(); + assertNotNull(abilities); + assertEquals(2, abilities.size()); + assertTrue(abilities.contains("speed")); + assertTrue(abilities.contains("strength")); + + } + + /** + * Build a new wizard {@link Character} without weapon and abilities and verify if it has the + * expected attributes + */ + @Test + public void testBuildPoorWarrior() { + final Character character = CharacterStepBuilder.newBuilder() + .name("Poor warrior") + .fighterClass("none") + .noWeapon() + .build(); + + assertEquals("Poor warrior", character.getName()); + assertEquals("none", character.getFighterClass()); + assertNull(character.getWeapon()); + assertNull(character.getAbilities()); + assertNotNull(character.toString()); + + } + + /** + * Build a new warrior {@link Character} without any abilities, but with a weapon and verify if it + * has the expected attributes + */ + @Test + public void testBuildWeakWarrior() { + final Character character = CharacterStepBuilder.newBuilder() + .name("Weak warrior") + .fighterClass("none") + .withWeapon("Slingshot") + .noAbilities() + .build(); + + assertEquals("Weak warrior", character.getName()); + assertEquals("none", character.getFighterClass()); + assertEquals("Slingshot", character.getWeapon()); + assertNull(character.getAbilities()); + assertNotNull(character.toString()); + + } + +} \ No newline at end of file diff --git a/strategy/README.md b/strategy/README.md new file mode 100644 index 000000000..f07397f67 --- /dev/null +++ b/strategy/README.md @@ -0,0 +1,34 @@ +--- +layout: pattern +title: Strategy +folder: strategy +permalink: /patterns/strategy/ +categories: Behavioral +tags: + - Java + - Difficulty-Beginner + - Gang Of Four +--- + +## Also known as +Policy + +## Intent +Define a family of algorithms, encapsulate each one, and make them +interchangeable. Strategy lets the algorithm vary independently from clients +that use it. + +![alt text](./etc/strategy_1.png "Strategy") + +## Applicability +Use the Strategy pattern when + +* many related classes differ only in their behavior. Strategies provide a way to configure a class either one of many behaviors +* you need different variants of an algorithm. for example, you might define algorithms reflecting different space/time trade-offs. Strategies can be used when these variants are implemented as a class hierarchy of algorithms +* an algorithm uses data that clients shouldn't know about. Use the Strategy pattern to avoid exposing complex, algorithm-specific data structures +* a class defines many behaviors, and these appear as multiple conditional statements in its operations. Instead of many conditionals, move related conditional branches into their own Strategy class + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) +* [Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions](http://www.amazon.com/Functional-Programming-Java-Harnessing-Expressions/dp/1937785467/ref=sr_1_1) diff --git a/strategy/pom.xml b/strategy/pom.xml index 466262d9b..e6fa58081 100644 --- a/strategy/pom.xml +++ b/strategy/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT strategy @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/strategy/src/main/java/com/iluwatar/strategy/App.java b/strategy/src/main/java/com/iluwatar/strategy/App.java index fc9a31327..be8826fe3 100644 --- a/strategy/src/main/java/com/iluwatar/strategy/App.java +++ b/strategy/src/main/java/com/iluwatar/strategy/App.java @@ -1,22 +1,71 @@ -package com.iluwatar.strategy; - -/** - * - * Strategy (DragonSlayingStrategy) encapsulates an algorithm. The containing - * object (DragonSlayer) can alter its behavior by changing its strategy. - * - */ -public class App { - - public static void main(String[] args) { - System.out.println("Green dragon spotted ahead!"); - DragonSlayer dragonSlayer = new DragonSlayer(new MeleeStrategy()); - dragonSlayer.goToBattle(); - System.out.println("Red dragon emerges."); - dragonSlayer.changeStrategy(new ProjectileStrategy()); - dragonSlayer.goToBattle(); - System.out.println("Black dragon lands before you."); - dragonSlayer.changeStrategy(new SpellStrategy()); - dragonSlayer.goToBattle(); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.strategy; + +/** + * + * The Strategy pattern (also known as the policy pattern) is a software design pattern that enables + * an algorithm's behavior to be selected at runtime. + *

    + * Before Java 8 the Strategies needed to be separate classes forcing the developer + * to write lots of boilerplate code. With modern Java it is easy to pass behavior + * with method references and lambdas making the code shorter and more readable. + *

    + * In this example ({@link DragonSlayingStrategy}) encapsulates an algorithm. The containing object + * ({@link DragonSlayer}) can alter its behavior by changing its strategy. + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + // GoF Strategy pattern + System.out.println("Green dragon spotted ahead!"); + DragonSlayer dragonSlayer = new DragonSlayer(new MeleeStrategy()); + dragonSlayer.goToBattle(); + System.out.println("Red dragon emerges."); + dragonSlayer.changeStrategy(new ProjectileStrategy()); + dragonSlayer.goToBattle(); + System.out.println("Black dragon lands before you."); + dragonSlayer.changeStrategy(new SpellStrategy()); + dragonSlayer.goToBattle(); + + // Java 8 Strategy pattern + System.out.println("Green dragon spotted ahead!"); + dragonSlayer = new DragonSlayer( + () -> System.out.println("With your Excalibur you severe the dragon's head!")); + dragonSlayer.goToBattle(); + System.out.println("Red dragon emerges."); + dragonSlayer.changeStrategy(() -> System.out.println( + "You shoot the dragon with the magical crossbow and it falls dead on the ground!")); + dragonSlayer.goToBattle(); + System.out.println("Black dragon lands before you."); + dragonSlayer.changeStrategy(() -> System.out.println( + "You cast the spell of disintegration and the dragon vaporizes in a pile of dust!")); + dragonSlayer.goToBattle(); + } +} diff --git a/strategy/src/main/java/com/iluwatar/strategy/DragonSlayer.java b/strategy/src/main/java/com/iluwatar/strategy/DragonSlayer.java index fc255dee0..93214ffbf 100644 --- a/strategy/src/main/java/com/iluwatar/strategy/DragonSlayer.java +++ b/strategy/src/main/java/com/iluwatar/strategy/DragonSlayer.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.strategy; /** @@ -7,17 +29,17 @@ package com.iluwatar.strategy; */ public class DragonSlayer { - private DragonSlayingStrategy strategy; + private DragonSlayingStrategy strategy; - public DragonSlayer(DragonSlayingStrategy strategy) { - this.strategy = strategy; - } + public DragonSlayer(DragonSlayingStrategy strategy) { + this.strategy = strategy; + } - public void changeStrategy(DragonSlayingStrategy strategy) { - this.strategy = strategy; - } + public void changeStrategy(DragonSlayingStrategy strategy) { + this.strategy = strategy; + } - public void goToBattle() { - strategy.execute(); - } + public void goToBattle() { + strategy.execute(); + } } diff --git a/strategy/src/main/java/com/iluwatar/strategy/DragonSlayingStrategy.java b/strategy/src/main/java/com/iluwatar/strategy/DragonSlayingStrategy.java index 28cea2aca..23e296279 100644 --- a/strategy/src/main/java/com/iluwatar/strategy/DragonSlayingStrategy.java +++ b/strategy/src/main/java/com/iluwatar/strategy/DragonSlayingStrategy.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.strategy; /** @@ -5,8 +27,9 @@ package com.iluwatar.strategy; * Strategy interface. * */ +@FunctionalInterface public interface DragonSlayingStrategy { - void execute(); + void execute(); } diff --git a/strategy/src/main/java/com/iluwatar/strategy/MeleeStrategy.java b/strategy/src/main/java/com/iluwatar/strategy/MeleeStrategy.java index 6c2aa550e..d17ff9041 100644 --- a/strategy/src/main/java/com/iluwatar/strategy/MeleeStrategy.java +++ b/strategy/src/main/java/com/iluwatar/strategy/MeleeStrategy.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.strategy; /** @@ -7,9 +29,8 @@ package com.iluwatar.strategy; */ public class MeleeStrategy implements DragonSlayingStrategy { - @Override - public void execute() { - System.out.println("With your Excalibur you severe the dragon's head!"); - } - + @Override + public void execute() { + System.out.println("With your Excalibur you sever the dragon's head!"); + } } diff --git a/strategy/src/main/java/com/iluwatar/strategy/ProjectileStrategy.java b/strategy/src/main/java/com/iluwatar/strategy/ProjectileStrategy.java index eab93b16e..7cd731167 100644 --- a/strategy/src/main/java/com/iluwatar/strategy/ProjectileStrategy.java +++ b/strategy/src/main/java/com/iluwatar/strategy/ProjectileStrategy.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.strategy; /** @@ -7,10 +29,9 @@ package com.iluwatar.strategy; */ public class ProjectileStrategy implements DragonSlayingStrategy { - @Override - public void execute() { - System.out - .println("You shoot the dragon with the magical crossbow and it falls dead on the ground!"); - } - + @Override + public void execute() { + System.out + .println("You shoot the dragon with the magical crossbow and it falls dead on the ground!"); + } } diff --git a/strategy/src/main/java/com/iluwatar/strategy/SpellStrategy.java b/strategy/src/main/java/com/iluwatar/strategy/SpellStrategy.java index 4a74b9b0d..6309ed31b 100644 --- a/strategy/src/main/java/com/iluwatar/strategy/SpellStrategy.java +++ b/strategy/src/main/java/com/iluwatar/strategy/SpellStrategy.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.strategy; /** @@ -7,10 +29,10 @@ package com.iluwatar.strategy; */ public class SpellStrategy implements DragonSlayingStrategy { - @Override - public void execute() { - System.out - .println("You cast the spell of disintegration and the dragon vaporizes in a pile of dust!"); - } + @Override + public void execute() { + System.out + .println("You cast the spell of disintegration and the dragon vaporizes in a pile of dust!"); + } } diff --git a/strategy/src/test/java/com/iluwatar/strategy/AppTest.java b/strategy/src/test/java/com/iluwatar/strategy/AppTest.java index e46708c0a..fa81ae747 100644 --- a/strategy/src/test/java/com/iluwatar/strategy/AppTest.java +++ b/strategy/src/test/java/com/iluwatar/strategy/AppTest.java @@ -1,14 +1,39 @@ -package com.iluwatar.strategy; - -import org.junit.Test; - -import com.iluwatar.strategy.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.strategy; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/strategy/src/test/java/com/iluwatar/strategy/DragonSlayerTest.java b/strategy/src/test/java/com/iluwatar/strategy/DragonSlayerTest.java new file mode 100644 index 000000000..ff7e6840a --- /dev/null +++ b/strategy/src/test/java/com/iluwatar/strategy/DragonSlayerTest.java @@ -0,0 +1,70 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.strategy; + +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/29/15 - 10:50 PM + * + * @author Jeroen Meulemeester + */ +public class DragonSlayerTest { + + /** + * Verify if the dragon slayer uses the strategy during battle + */ + @Test + public void testGoToBattle() { + final DragonSlayingStrategy strategy = mock(DragonSlayingStrategy.class); + final DragonSlayer dragonSlayer = new DragonSlayer(strategy); + + dragonSlayer.goToBattle(); + verify(strategy).execute(); + verifyNoMoreInteractions(strategy); + } + + /** + * Verify if the dragon slayer uses the new strategy during battle after a change of strategy + */ + @Test + public void testChangeStrategy() throws Exception { + final DragonSlayingStrategy initialStrategy = mock(DragonSlayingStrategy.class); + final DragonSlayer dragonSlayer = new DragonSlayer(initialStrategy); + + dragonSlayer.goToBattle(); + verify(initialStrategy).execute(); + + final DragonSlayingStrategy newStrategy = mock(DragonSlayingStrategy.class); + dragonSlayer.changeStrategy(newStrategy); + + dragonSlayer.goToBattle(); + verify(newStrategy).execute(); + + verifyNoMoreInteractions(initialStrategy, newStrategy); + } +} \ No newline at end of file diff --git a/strategy/src/test/java/com/iluwatar/strategy/DragonSlayingStrategyTest.java b/strategy/src/test/java/com/iluwatar/strategy/DragonSlayingStrategyTest.java new file mode 100644 index 000000000..35f4c1a82 --- /dev/null +++ b/strategy/src/test/java/com/iluwatar/strategy/DragonSlayingStrategyTest.java @@ -0,0 +1,126 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.strategy; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.PrintStream; +import java.util.Arrays; +import java.util.Collection; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/29/15 - 10:58 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class DragonSlayingStrategyTest { + + /** + * @return The test parameters for each cycle + */ + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList( + new Object[]{ + new MeleeStrategy(), + "With your Excalibur you sever the dragon's head!" + }, + new Object[]{ + new ProjectileStrategy(), + "You shoot the dragon with the magical crossbow and it falls dead on the ground!" + }, + new Object[]{ + new SpellStrategy(), + "You cast the spell of disintegration and the dragon vaporizes in a pile of dust!" + } + ); + } + + /** + * The tested strategy + */ + private final DragonSlayingStrategy strategy; + + /** + * The expected action on the std-out + */ + private final String expectedResult; + + /** + * The mocked standard out {@link PrintStream}, required since some actions don't have any + * influence on accessible objects, except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Create a new test instance for the given strategy + * + * @param strategy The tested strategy + * @param expectedResult The expected result + */ + public DragonSlayingStrategyTest(final DragonSlayingStrategy strategy, final String expectedResult) { + this.strategy = strategy; + this.expectedResult = expectedResult; + } + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Test if executing the strategy gives the correct response + */ + @Test + public void testExecute() { + this.strategy.execute(); + verify(this.stdOutMock).println(this.expectedResult); + verifyNoMoreInteractions(this.stdOutMock); + } + +} \ No newline at end of file diff --git a/template-method/README.md b/template-method/README.md new file mode 100644 index 000000000..ad972f06b --- /dev/null +++ b/template-method/README.md @@ -0,0 +1,29 @@ +--- +layout: pattern +title: Template method +folder: template-method +permalink: /patterns/template-method/ +categories: Behavioral +tags: + - Java + - Difficulty-Beginner + - Gang Of Four +--- + +## Intent +Define the skeleton of an algorithm in an operation, deferring some +steps to subclasses. Template method lets subclasses redefine certain steps of +an algorithm without changing the algorithm's structure. + +![alt text](./etc/template-method_1.png "Template Method") + +## Applicability +The Template Method pattern should be used + +* to implement the invariant parts of an algorithm once and leave it up to subclasses to implement the behavior that can vary +* when common behavior among subclasses should be factored and localized in a common class to avoid code duplication. This is good example of "refactoring to generalize" as described by Opdyke and Johnson. You first identify the differences in the existing code and then separate the differences into new operations. Finally, you replace the differing code with a template method that calls one of these new operations +* to control subclasses extensions. You can define a template method that calls "hook" operations at specific points, thereby permitting extensions only at those points + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/template-method/pom.xml b/template-method/pom.xml index 7280b6cf5..ee533df8f 100644 --- a/template-method/pom.xml +++ b/template-method/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT template-method @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/template-method/src/main/java/com/iluwatar/templatemethod/App.java b/template-method/src/main/java/com/iluwatar/templatemethod/App.java index 9797474cf..ead9c64f4 100644 --- a/template-method/src/main/java/com/iluwatar/templatemethod/App.java +++ b/template-method/src/main/java/com/iluwatar/templatemethod/App.java @@ -1,20 +1,47 @@ -package com.iluwatar.templatemethod; - -/** - * - * Template Method defines a skeleton for an algorithm. The algorithm subclasses - * provide implementation for the blank parts. - * - * In this example HalflingThief contains StealingMethod that can be changed. - * First the thief hits with HitAndRunMethod and then with SubtleMethod. - * - */ -public class App { - - public static void main(String[] args) { - HalflingThief thief = new HalflingThief(new HitAndRunMethod()); - thief.steal(); - thief.changeMethod(new SubtleMethod()); - thief.steal(); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.templatemethod; + +/** + * + * Template Method defines a skeleton for an algorithm. The algorithm subclasses provide + * implementation for the blank parts. + *

    + * In this example {@link HalflingThief} contains {@link StealingMethod} that can be changed. First + * the thief hits with {@link HitAndRunMethod} and then with {@link SubtleMethod}. + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + HalflingThief thief = new HalflingThief(new HitAndRunMethod()); + thief.steal(); + thief.changeMethod(new SubtleMethod()); + thief.steal(); + } +} diff --git a/template-method/src/main/java/com/iluwatar/templatemethod/HalflingThief.java b/template-method/src/main/java/com/iluwatar/templatemethod/HalflingThief.java index 3cdb2f1b1..e776044f6 100644 --- a/template-method/src/main/java/com/iluwatar/templatemethod/HalflingThief.java +++ b/template-method/src/main/java/com/iluwatar/templatemethod/HalflingThief.java @@ -1,23 +1,45 @@ -package com.iluwatar.templatemethod; - -/** - * - * Halfling thief uses StealingMethod to steal. - * - */ -public class HalflingThief { - - private StealingMethod method; - - public HalflingThief(StealingMethod method) { - this.method = method; - } - - public void steal() { - method.steal(); - } - - public void changeMethod(StealingMethod method) { - this.method = method; - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.templatemethod; + +/** + * + * Halfling thief uses {@link StealingMethod} to steal. + * + */ +public class HalflingThief { + + private StealingMethod method; + + public HalflingThief(StealingMethod method) { + this.method = method; + } + + public void steal() { + method.steal(); + } + + public void changeMethod(StealingMethod method) { + this.method = method; + } +} diff --git a/template-method/src/main/java/com/iluwatar/templatemethod/HitAndRunMethod.java b/template-method/src/main/java/com/iluwatar/templatemethod/HitAndRunMethod.java index 78ee2031b..7a78576a1 100644 --- a/template-method/src/main/java/com/iluwatar/templatemethod/HitAndRunMethod.java +++ b/template-method/src/main/java/com/iluwatar/templatemethod/HitAndRunMethod.java @@ -1,25 +1,46 @@ -package com.iluwatar.templatemethod; - -/** - * - * HitAndRunMethod implementation of StealingMethod. - * - */ -public class HitAndRunMethod extends StealingMethod { - - @Override - protected String pickTarget() { - return "old goblin woman"; - } - - @Override - protected void confuseTarget(String target) { - System.out.println("Approach the " + target + " from behind."); - } - - @Override - protected void stealTheItem(String target) { - System.out.println("Grab the handbag and run away fast!"); - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.templatemethod; + +/** + * + * HitAndRunMethod implementation of {@link StealingMethod}. + * + */ +public class HitAndRunMethod extends StealingMethod { + + @Override + protected String pickTarget() { + return "old goblin woman"; + } + + @Override + protected void confuseTarget(String target) { + System.out.println("Approach the " + target + " from behind."); + } + + @Override + protected void stealTheItem(String target) { + System.out.println("Grab the handbag and run away fast!"); + } +} diff --git a/template-method/src/main/java/com/iluwatar/templatemethod/StealingMethod.java b/template-method/src/main/java/com/iluwatar/templatemethod/StealingMethod.java index 20e077750..c8c584cdd 100644 --- a/template-method/src/main/java/com/iluwatar/templatemethod/StealingMethod.java +++ b/template-method/src/main/java/com/iluwatar/templatemethod/StealingMethod.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.templatemethod; /** @@ -7,16 +29,19 @@ package com.iluwatar.templatemethod; */ public abstract class StealingMethod { - protected abstract String pickTarget(); + protected abstract String pickTarget(); - protected abstract void confuseTarget(String target); + protected abstract void confuseTarget(String target); - protected abstract void stealTheItem(String target); + protected abstract void stealTheItem(String target); - public void steal() { - String target = pickTarget(); - System.out.println("The target has been chosen as " + target + "."); - confuseTarget(target); - stealTheItem(target); - } + /** + * Steal + */ + public void steal() { + String target = pickTarget(); + System.out.println("The target has been chosen as " + target + "."); + confuseTarget(target); + stealTheItem(target); + } } diff --git a/template-method/src/main/java/com/iluwatar/templatemethod/SubtleMethod.java b/template-method/src/main/java/com/iluwatar/templatemethod/SubtleMethod.java index c5bd1cfaf..4fdb5d758 100644 --- a/template-method/src/main/java/com/iluwatar/templatemethod/SubtleMethod.java +++ b/template-method/src/main/java/com/iluwatar/templatemethod/SubtleMethod.java @@ -1,27 +1,46 @@ -package com.iluwatar.templatemethod; - -/** - * - * SubtleMethod implementation of StealingMethod. - * - */ -public class SubtleMethod extends StealingMethod { - - @Override - protected String pickTarget() { - return "shop keeper"; - } - - @Override - protected void confuseTarget(String target) { - System.out.println("Approach the " + target - + " with tears running and hug him!"); - } - - @Override - protected void stealTheItem(String target) { - System.out.println("While in close contact grab the " + target - + "'s wallet."); - } - -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.templatemethod; + +/** + * + * SubtleMethod implementation of {@link StealingMethod}. + * + */ +public class SubtleMethod extends StealingMethod { + + @Override + protected String pickTarget() { + return "shop keeper"; + } + + @Override + protected void confuseTarget(String target) { + System.out.println("Approach the " + target + " with tears running and hug him!"); + } + + @Override + protected void stealTheItem(String target) { + System.out.println("While in close contact grab the " + target + "'s wallet."); + } +} diff --git a/template-method/src/test/java/com/iluwatar/templatemethod/AppTest.java b/template-method/src/test/java/com/iluwatar/templatemethod/AppTest.java index fce07bef1..e0baf40cb 100644 --- a/template-method/src/test/java/com/iluwatar/templatemethod/AppTest.java +++ b/template-method/src/test/java/com/iluwatar/templatemethod/AppTest.java @@ -1,14 +1,39 @@ -package com.iluwatar.templatemethod; - -import org.junit.Test; - -import com.iluwatar.templatemethod.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.templatemethod; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/template-method/src/test/java/com/iluwatar/templatemethod/HalflingThiefTest.java b/template-method/src/test/java/com/iluwatar/templatemethod/HalflingThiefTest.java new file mode 100644 index 000000000..31cb078e3 --- /dev/null +++ b/template-method/src/test/java/com/iluwatar/templatemethod/HalflingThiefTest.java @@ -0,0 +1,72 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.templatemethod; + +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/29/15 - 18:15 PM + * + * @author Jeroen Meulemeester + */ +public class HalflingThiefTest { + + /** + * Verify if the thief uses the provided stealing method + */ + @Test + public void testSteal() { + final StealingMethod method = mock(StealingMethod.class); + final HalflingThief thief = new HalflingThief(method); + + thief.steal(); + verify(method).steal(); + + verifyNoMoreInteractions(method); + } + + /** + * Verify if the thief uses the provided stealing method, and the new method after changing it + */ + @Test + public void testChangeMethod() { + final StealingMethod initialMethod = mock(StealingMethod.class); + final HalflingThief thief = new HalflingThief(initialMethod); + + thief.steal(); + verify(initialMethod).steal(); + + final StealingMethod newMethod = mock(StealingMethod.class); + thief.changeMethod(newMethod); + + thief.steal(); + verify(newMethod).steal(); + + verifyNoMoreInteractions(initialMethod, newMethod); + + } +} \ No newline at end of file diff --git a/template-method/src/test/java/com/iluwatar/templatemethod/HitAndRunMethodTest.java b/template-method/src/test/java/com/iluwatar/templatemethod/HitAndRunMethodTest.java new file mode 100644 index 000000000..27d601ac3 --- /dev/null +++ b/template-method/src/test/java/com/iluwatar/templatemethod/HitAndRunMethodTest.java @@ -0,0 +1,45 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.templatemethod; + +/** + * Date: 12/30/15 - 18:12 PM + * + * @author Jeroen Meulemeester + */ +public class HitAndRunMethodTest extends StealingMethodTest { + + /** + * Create a new test for the {@link HitAndRunMethod} + */ + public HitAndRunMethodTest() { + super( + new HitAndRunMethod(), + "old goblin woman", + "The target has been chosen as old goblin woman.", + "Approach the old goblin woman from behind.", + "Grab the handbag and run away fast!" + ); + } + +} \ No newline at end of file diff --git a/template-method/src/test/java/com/iluwatar/templatemethod/StealingMethodTest.java b/template-method/src/test/java/com/iluwatar/templatemethod/StealingMethodTest.java new file mode 100644 index 000000000..e0cb90d42 --- /dev/null +++ b/template-method/src/test/java/com/iluwatar/templatemethod/StealingMethodTest.java @@ -0,0 +1,164 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.templatemethod; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; + +import java.io.PrintStream; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/30/15 - 18:12 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StealingMethodTest { + + /** + * The tested stealing method + */ + private final M method; + + /** + * The expected target + */ + private final String expectedTarget; + + /** + * The expected target picking result + */ + private final String expectedTargetResult; + + /** + * The expected confusion method + */ + private final String expectedConfuseMethod; + + /** + * The expected stealing method + */ + private final String expectedStealMethod; + + /** + * The mocked standard out {@link PrintStream}, required since some actions don't have any + * influence on accessible objects, except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Create a new test for the given stealing method, together with the expected results + * + * @param method The tested stealing method + * @param expectedTarget The expected target name + * @param expectedTargetResult The expected target picking result + * @param expectedConfuseMethod The expected confusion method + * @param expectedStealMethod The expected stealing method + */ + public StealingMethodTest(final M method, String expectedTarget, final String expectedTargetResult, + final String expectedConfuseMethod, final String expectedStealMethod) { + + this.method = method; + this.expectedTarget = expectedTarget; + this.expectedTargetResult = expectedTargetResult; + this.expectedConfuseMethod = expectedConfuseMethod; + this.expectedStealMethod = expectedStealMethod; + } + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Verify if the thief picks the correct target + */ + @Test + public void testPickTarget() { + assertEquals(expectedTarget, this.method.pickTarget()); + } + + /** + * Verify if the target confusing step goes as planned + */ + @Test + public void testConfuseTarget() { + verifyZeroInteractions(this.stdOutMock); + + this.method.confuseTarget(this.expectedTarget); + verify(this.stdOutMock).println(this.expectedConfuseMethod); + verifyNoMoreInteractions(this.stdOutMock); + } + + /** + * Verify if the stealing step goes as planned + */ + @Test + public void testStealTheItem() { + verifyZeroInteractions(this.stdOutMock); + + this.method.stealTheItem(this.expectedTarget); + verify(this.stdOutMock).println(this.expectedStealMethod); + verifyNoMoreInteractions(this.stdOutMock); + } + + /** + * Verify if the complete steal process goes as planned + */ + @Test + public void testSteal() { + final InOrder inOrder = inOrder(this.stdOutMock); + + this.method.steal(); + + inOrder.verify(this.stdOutMock).println(this.expectedTargetResult); + inOrder.verify(this.stdOutMock).println(this.expectedConfuseMethod); + inOrder.verify(this.stdOutMock).println(this.expectedStealMethod); + inOrder.verifyNoMoreInteractions(); + } + +} \ No newline at end of file diff --git a/template-method/src/test/java/com/iluwatar/templatemethod/SubtleMethodTest.java b/template-method/src/test/java/com/iluwatar/templatemethod/SubtleMethodTest.java new file mode 100644 index 000000000..78c86adfc --- /dev/null +++ b/template-method/src/test/java/com/iluwatar/templatemethod/SubtleMethodTest.java @@ -0,0 +1,45 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.templatemethod; + +/** + * Date: 12/30/15 - 18:19 PM + * + * @author Jeroen Meulemeester + */ +public class SubtleMethodTest extends StealingMethodTest { + + /** + * Create a new test for the {@link SubtleMethod} + */ + public SubtleMethodTest() { + super( + new SubtleMethod(), + "shop keeper", + "The target has been chosen as shop keeper.", + "Approach the shop keeper with tears running and hug him!", + "While in close contact grab the shop keeper's wallet." + ); + } + +} \ No newline at end of file diff --git a/thread-pool/README.md b/thread-pool/README.md new file mode 100644 index 000000000..9806fa8e0 --- /dev/null +++ b/thread-pool/README.md @@ -0,0 +1,25 @@ +--- +layout: pattern +title: Thread Pool +folder: thread-pool +permalink: /patterns/thread-pool/ +categories: Concurrency +tags: + - Java + - Difficulty-Intermediate + - Performance +--- + +## Intent +It is often the case that tasks to be executed are short-lived and +the number of tasks is large. Creating a new thread for each task would make +the system spend more time creating and destroying the threads than executing +the actual tasks. Thread Pool solves this problem by reusing existing threads +and eliminating the latency of creating new threads. + +![alt text](./etc/thread-pool.png "Thread Pool") + +## Applicability +Use the Thread Pool pattern when + +* you have a large number of short-lived tasks to be executed in parallel diff --git a/thread-pool/pom.xml b/thread-pool/pom.xml index 71a357ab9..e7fa43103 100644 --- a/thread-pool/pom.xml +++ b/thread-pool/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT thread-pool @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/thread-pool/src/main/java/com/iluwatar/threadpool/App.java b/thread-pool/src/main/java/com/iluwatar/threadpool/App.java index 0a7bed3a0..16fbca35a 100644 --- a/thread-pool/src/main/java/com/iluwatar/threadpool/App.java +++ b/thread-pool/src/main/java/com/iluwatar/threadpool/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.threadpool; import java.util.ArrayList; @@ -7,59 +29,66 @@ import java.util.concurrent.Executors; /** * - * Thread Pool pattern is where a number of threads are created to perform a number of tasks, - * which are usually organized in a queue. The results from the tasks being executed might - * also be placed in a queue, or the tasks might return no result. Typically, there are many - * more tasks than threads. As soon as a thread completes its task, it will request the next - * task from the queue until all tasks have been completed. The thread can then terminate, or - * sleep until there are new tasks available. - * - * In this example we create a list of tasks presenting work to be done. Each task is then - * wrapped into a Worker object that implements Runnable. We create an ExecutorService with - * fixed number of threads (Thread Pool) and use them to execute the Workers. + * Thread Pool pattern is where a number of threads are created to perform a number of tasks, which + * are usually organized in a queue. The results from the tasks being executed might also be placed + * in a queue, or the tasks might return no result. Typically, there are many more tasks than + * threads. As soon as a thread completes its task, it will request the next task from the queue + * until all tasks have been completed. The thread can then terminate, or sleep until there are new + * tasks available. + *

    + * In this example we create a list of tasks presenting work to be done. Each task is then wrapped + * into a {@link Worker} object that implements {@link Runnable}. We create an + * {@link ExecutorService} with fixed number of threads (Thread Pool) and use them to execute the + * {@link Worker}s. * */ public class App { - - public static void main( String[] args ) { - - System.out.println("Program started"); - - // Create a list of tasks to be executed - List tasks = new ArrayList<>(); - tasks.add(new PotatoPeelingTask(3)); - tasks.add(new PotatoPeelingTask(6)); - tasks.add(new CoffeeMakingTask(2)); - tasks.add(new CoffeeMakingTask(6)); - tasks.add(new PotatoPeelingTask(4)); - tasks.add(new CoffeeMakingTask(2)); - tasks.add(new PotatoPeelingTask(4)); - tasks.add(new CoffeeMakingTask(9)); - tasks.add(new PotatoPeelingTask(3)); - tasks.add(new CoffeeMakingTask(2)); - tasks.add(new PotatoPeelingTask(4)); - tasks.add(new CoffeeMakingTask(2)); - tasks.add(new CoffeeMakingTask(7)); - tasks.add(new PotatoPeelingTask(4)); - tasks.add(new PotatoPeelingTask(5)); - - // Creates a thread pool that reuses a fixed number of threads operating off a shared - // unbounded queue. At any point, at most nThreads threads will be active processing - // tasks. If additional tasks are submitted when all threads are active, they will wait - // in the queue until a thread is available. - ExecutorService executor = Executors.newFixedThreadPool(3); - - // Allocate new worker for each task - // The worker is executed when a thread becomes - // available in the thread pool - for (int i=0; i tasks = new ArrayList<>(); + tasks.add(new PotatoPeelingTask(3)); + tasks.add(new PotatoPeelingTask(6)); + tasks.add(new CoffeeMakingTask(2)); + tasks.add(new CoffeeMakingTask(6)); + tasks.add(new PotatoPeelingTask(4)); + tasks.add(new CoffeeMakingTask(2)); + tasks.add(new PotatoPeelingTask(4)); + tasks.add(new CoffeeMakingTask(9)); + tasks.add(new PotatoPeelingTask(3)); + tasks.add(new CoffeeMakingTask(2)); + tasks.add(new PotatoPeelingTask(4)); + tasks.add(new CoffeeMakingTask(2)); + tasks.add(new CoffeeMakingTask(7)); + tasks.add(new PotatoPeelingTask(4)); + tasks.add(new PotatoPeelingTask(5)); + + // Creates a thread pool that reuses a fixed number of threads operating off a shared + // unbounded queue. At any point, at most nThreads threads will be active processing + // tasks. If additional tasks are submitted when all threads are active, they will wait + // in the queue until a thread is available. + ExecutorService executor = Executors.newFixedThreadPool(3); + + // Allocate new worker for each task + // The worker is executed when a thread becomes + // available in the thread pool + for (int i = 0; i < tasks.size(); i++) { + Runnable worker = new Worker(tasks.get(i)); + executor.execute(worker); } + // All tasks were executed, now shutdown + executor.shutdown(); + while (!executor.isTerminated()) { + Thread.yield(); + } + System.out.println("Program finished"); + } } diff --git a/thread-pool/src/main/java/com/iluwatar/threadpool/CoffeeMakingTask.java b/thread-pool/src/main/java/com/iluwatar/threadpool/CoffeeMakingTask.java index 9bbabfd0d..fce9ada9c 100644 --- a/thread-pool/src/main/java/com/iluwatar/threadpool/CoffeeMakingTask.java +++ b/thread-pool/src/main/java/com/iluwatar/threadpool/CoffeeMakingTask.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.threadpool; /** @@ -7,14 +29,14 @@ package com.iluwatar.threadpool; */ public class CoffeeMakingTask extends Task { - private static final int TIME_PER_CUP = 300; - - public CoffeeMakingTask(int numCups) { - super(numCups * TIME_PER_CUP); - } + private static final int TIME_PER_CUP = 100; - @Override - public String toString() { - return String.format("%s %s", this.getClass().getSimpleName(), super.toString()); - } + public CoffeeMakingTask(int numCups) { + super(numCups * TIME_PER_CUP); + } + + @Override + public String toString() { + return String.format("%s %s", this.getClass().getSimpleName(), super.toString()); + } } diff --git a/thread-pool/src/main/java/com/iluwatar/threadpool/PotatoPeelingTask.java b/thread-pool/src/main/java/com/iluwatar/threadpool/PotatoPeelingTask.java index 6b2169961..e55debe28 100644 --- a/thread-pool/src/main/java/com/iluwatar/threadpool/PotatoPeelingTask.java +++ b/thread-pool/src/main/java/com/iluwatar/threadpool/PotatoPeelingTask.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.threadpool; /** @@ -7,14 +29,14 @@ package com.iluwatar.threadpool; */ public class PotatoPeelingTask extends Task { - private static final int TIME_PER_POTATO = 500; - - public PotatoPeelingTask(int numPotatoes) { - super(numPotatoes * TIME_PER_POTATO); - } - - @Override - public String toString() { - return String.format("%s %s", this.getClass().getSimpleName(), super.toString()); - } + private static final int TIME_PER_POTATO = 200; + + public PotatoPeelingTask(int numPotatoes) { + super(numPotatoes * TIME_PER_POTATO); + } + + @Override + public String toString() { + return String.format("%s %s", this.getClass().getSimpleName(), super.toString()); + } } diff --git a/thread-pool/src/main/java/com/iluwatar/threadpool/Task.java b/thread-pool/src/main/java/com/iluwatar/threadpool/Task.java index 4766b6eee..623d2b78e 100644 --- a/thread-pool/src/main/java/com/iluwatar/threadpool/Task.java +++ b/thread-pool/src/main/java/com/iluwatar/threadpool/Task.java @@ -1,32 +1,56 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.threadpool; +import java.util.concurrent.atomic.AtomicInteger; + /** - * + * * Abstract base class for tasks * */ public abstract class Task { - private static int nextId = 1; - - private final int id; - private final int timeMs; - - public Task(final int timeMs) { - this.id = nextId++; - this.timeMs = timeMs; - } - - public int getId() { - return id; - } - - public int getTimeMs() { - return timeMs; - } - - @Override - public String toString() { - return String.format("id=%d timeMs=%d", id, timeMs); - } + private static final AtomicInteger ID_GENERATOR = new AtomicInteger(); + + private final int id; + private final int timeMs; + + public Task(final int timeMs) { + this.id = ID_GENERATOR.incrementAndGet(); + this.timeMs = timeMs; + } + + public int getId() { + return id; + } + + public int getTimeMs() { + return timeMs; + } + + @Override + public String toString() { + return String.format("id=%d timeMs=%d", id, timeMs); + } } diff --git a/thread-pool/src/main/java/com/iluwatar/threadpool/Worker.java b/thread-pool/src/main/java/com/iluwatar/threadpool/Worker.java index b6ee5e665..1354cab41 100644 --- a/thread-pool/src/main/java/com/iluwatar/threadpool/Worker.java +++ b/thread-pool/src/main/java/com/iluwatar/threadpool/Worker.java @@ -1,25 +1,48 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.threadpool; /** * - * Worker implements Runnable and thus can be executed by ExecutorService + * Worker implements {@link Runnable} and thus can be executed by {@link ExecutorService} * */ public class Worker implements Runnable { - - private final Task task; - public Worker(final Task task) { - this.task = task; - } - - @Override - public void run() { - System.out.println(String.format("%s processing %s", Thread.currentThread().getName(), task.toString())); - try { - Thread.sleep(task.getTimeMs()); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } + private final Task task; + + public Worker(final Task task) { + this.task = task; + } + + @Override + public void run() { + System.out.println(String.format("%s processing %s", Thread.currentThread().getName(), + task.toString())); + try { + Thread.sleep(task.getTimeMs()); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } } diff --git a/thread-pool/src/test/java/com/iluwatar/threadpool/AppTest.java b/thread-pool/src/test/java/com/iluwatar/threadpool/AppTest.java index ec5c031ff..5536d6631 100644 --- a/thread-pool/src/test/java/com/iluwatar/threadpool/AppTest.java +++ b/thread-pool/src/test/java/com/iluwatar/threadpool/AppTest.java @@ -1,14 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.threadpool; import org.junit.Test; -import com.iluwatar.threadpool.App; - +/** + * Application test + * + * @author ilkka + * + */ public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } + + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/thread-pool/src/test/java/com/iluwatar/threadpool/CoffeeMakingTaskTest.java b/thread-pool/src/test/java/com/iluwatar/threadpool/CoffeeMakingTaskTest.java new file mode 100644 index 000000000..281ef16ad --- /dev/null +++ b/thread-pool/src/test/java/com/iluwatar/threadpool/CoffeeMakingTaskTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.threadpool; + +/** + * Date: 12/30/15 - 18:23 PM + * + * @author Jeroen Meulemeester + */ +public class CoffeeMakingTaskTest extends TaskTest { + + /** + * Create a new test instance + */ + public CoffeeMakingTaskTest() { + super(CoffeeMakingTask::new, 100); + } + +} diff --git a/thread-pool/src/test/java/com/iluwatar/threadpool/PotatoPeelingTaskTest.java b/thread-pool/src/test/java/com/iluwatar/threadpool/PotatoPeelingTaskTest.java new file mode 100644 index 000000000..d27e6ba5d --- /dev/null +++ b/thread-pool/src/test/java/com/iluwatar/threadpool/PotatoPeelingTaskTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.threadpool; + +/** + * Date: 12/30/15 - 18:23 PM + * + * @author Jeroen Meulemeester + */ +public class PotatoPeelingTaskTest extends TaskTest { + + /** + * Create a new test instance + */ + public PotatoPeelingTaskTest() { + super(PotatoPeelingTask::new, 200); + } + +} \ No newline at end of file diff --git a/thread-pool/src/test/java/com/iluwatar/threadpool/TaskTest.java b/thread-pool/src/test/java/com/iluwatar/threadpool/TaskTest.java new file mode 100644 index 000000000..ded3e9d42 --- /dev/null +++ b/thread-pool/src/test/java/com/iluwatar/threadpool/TaskTest.java @@ -0,0 +1,143 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.threadpool; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/30/15 - 18:22 PM + * + * @author Jeroen Meulemeester + */ +public abstract class TaskTest { + + /** + * The number of tasks used during the concurrency test + */ + private static final int TASK_COUNT = 128 * 1024; + + /** + * The number of threads used during the concurrency test + */ + private static final int THREAD_COUNT = 8; + + /** + * The task factory, used to create new test items + */ + private final Function factory; + + /** + * The expected time needed to run the task 1 single time, in milli seconds + */ + private final int expectedExecutionTime; + + /** + * Create a new test instance + * + * @param factory The task factory, used to create new test items + * @param expectedExecutionTime The expected time needed to run the task 1 time, in milli seconds + */ + public TaskTest(final Function factory, final int expectedExecutionTime) { + this.factory = factory; + this.expectedExecutionTime = expectedExecutionTime; + } + + /** + * Verify if the generated id is unique for each task, even if the tasks are created in separate + * threads + */ + @Test(timeout = 10000) + public void testIdGeneration() throws Exception { + final ExecutorService service = Executors.newFixedThreadPool(THREAD_COUNT); + + final List> tasks = new ArrayList<>(); + for (int i = 0; i < TASK_COUNT; i++) { + tasks.add(() -> factory.apply(1).getId()); + } + + final List ids = service.invokeAll(tasks) + .stream() + .map(TaskTest::get) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + service.shutdownNow(); + + final long uniqueIdCount = ids.stream() + .distinct() + .count(); + + assertEquals(TASK_COUNT, ids.size()); + assertEquals(TASK_COUNT, uniqueIdCount); + + } + + /** + * Verify if the time per execution of a task matches the actual time required to execute the task + * a given number of times + */ + @Test + public void testTimeMs() { + for (int i = 0; i < 10; i++) { + assertEquals(this.expectedExecutionTime * i, this.factory.apply(i).getTimeMs()); + } + } + + /** + * Verify if the task has some sort of {@link T#toString()}, different from 'null' + */ + @Test + public void testToString() { + assertNotNull(this.factory.apply(0).toString()); + } + + /** + * Extract the result from a future or returns 'null' when an exception occurred + * + * @param future The future we want the result from + * @param The result type + * @return The result or 'null' when a checked exception occurred + */ + private static O get(Future future) { + try { + return future.get(); + } catch (InterruptedException | ExecutionException e) { + return null; + } + } + +} \ No newline at end of file diff --git a/thread-pool/src/test/java/com/iluwatar/threadpool/WorkerTest.java b/thread-pool/src/test/java/com/iluwatar/threadpool/WorkerTest.java new file mode 100644 index 000000000..24fe87548 --- /dev/null +++ b/thread-pool/src/test/java/com/iluwatar/threadpool/WorkerTest.java @@ -0,0 +1,53 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.threadpool; + +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/30/15 - 18:21 PM + * + * @author Jeroen Meulemeester + */ +public class WorkerTest { + + /** + * Verify if a worker does the actual job + */ + @Test + public void testRun() { + final Task task = mock(Task.class); + final Worker worker = new Worker(task); + verifyZeroInteractions(task); + + worker.run(); + verify(task).getTimeMs(); + verifyNoMoreInteractions(task); + } + +} \ No newline at end of file diff --git a/tolerant-reader/README.md b/tolerant-reader/README.md new file mode 100644 index 000000000..be0085f2c --- /dev/null +++ b/tolerant-reader/README.md @@ -0,0 +1,27 @@ +--- +layout: pattern +title: Tolerant Reader +folder: tolerant-reader +permalink: /patterns/tolerant-reader/ +categories: Integration +tags: + - Java + - Difficulty-Beginner +--- + +## Intent +Tolerant Reader is an integration pattern that helps creating +robust communication systems. The idea is to be as tolerant as possible when +reading data from another service. This way, when the communication schema +changes, the readers must not break. + +![alt text](./etc/tolerant-reader.png "Tolerant Reader") + +## Applicability +Use the Tolerant Reader pattern when + +* the communication schema can evolve and change and yet the receiving side should not break + +## Credits + +* [Martin Fowler - Tolerant Reader](http://martinfowler.com/bliki/TolerantReader.html) diff --git a/tolerant-reader/etc/tolerant-reader.png b/tolerant-reader/etc/tolerant-reader.png index 396b2559b..45f0302a2 100644 Binary files a/tolerant-reader/etc/tolerant-reader.png and b/tolerant-reader/etc/tolerant-reader.png differ diff --git a/tolerant-reader/etc/tolerant-reader.ucls b/tolerant-reader/etc/tolerant-reader.ucls index 1c9065583..92e069f8e 100644 --- a/tolerant-reader/etc/tolerant-reader.ucls +++ b/tolerant-reader/etc/tolerant-reader.ucls @@ -1,45 +1,47 @@ - - + + - - + + - - + + - - - - - - - - + + + + + + + + + - + diff --git a/tolerant-reader/pom.xml b/tolerant-reader/pom.xml index c92b169ba..e5dd3ba88 100644 --- a/tolerant-reader/pom.xml +++ b/tolerant-reader/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT tolerant-reader diff --git a/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/App.java b/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/App.java index ab4a8bfad..066b6e737 100644 --- a/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/App.java +++ b/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/App.java @@ -1,42 +1,71 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.tolerantreader; import java.io.IOException; /** * - * Tolerant Reader is an integration pattern that helps creating robust communication - * systems. The idea is to be as tolerant as possible when reading data from another - * service. This way, when the communication schema changes, the readers must not break. - * - * In this example we use Java serialization to write representations of RainbowFish - * objects to file. RainbowFish is the initial version which we can easily read and - * write using RainbowFishSerializer methods. RainbowFish then evolves to RainbowFishV2 - * and we again write it to file with a method designed to do just that. However, the reader - * client does not know about the new format and still reads with the method designed for - * V1 schema. Fortunately the reading method has been designed with the Tolerant Reader - * pattern and does not break even though RainbowFishV2 has new fields that are serialized. + * Tolerant Reader is an integration pattern that helps creating robust communication systems. The + * idea is to be as tolerant as possible when reading data from another service. This way, when the + * communication schema changes, the readers must not break. + *

    + * In this example we use Java serialization to write representations of {@link RainbowFish} objects + * to file. {@link RainbowFish} is the initial version which we can easily read and write using + * {@link RainbowFishSerializer} methods. {@link RainbowFish} then evolves to {@link RainbowFishV2} + * and we again write it to file with a method designed to do just that. However, the reader client + * does not know about the new format and still reads with the method designed for V1 schema. + * Fortunately the reading method has been designed with the Tolerant Reader pattern and does not + * break even though {@link RainbowFishV2} has new fields that are serialized. * */ public class App { - - public static void main( String[] args ) throws IOException, ClassNotFoundException { - // Write V1 - RainbowFish fishV1 = new RainbowFish("Zed", 10, 11, 12); - System.out.println(String.format("fishV1 name=%s age=%d length=%d weight=%d", fishV1.getName(), - fishV1.getAge(), fishV1.getLengthMeters(), fishV1.getWeightTons())); - RainbowFishSerializer.writeV1(fishV1, "fish1.out"); - // Read V1 - RainbowFish deserializedFishV1 = RainbowFishSerializer.readV1("fish1.out"); - System.out.println(String.format("deserializedFishV1 name=%s age=%d length=%d weight=%d", deserializedFishV1.getName(), - deserializedFishV1.getAge(), deserializedFishV1.getLengthMeters(), deserializedFishV1.getWeightTons())); - // Write V2 - RainbowFishV2 fishV2 = new RainbowFishV2("Scar", 5, 12, 15, true, true, true); - System.out.println(String.format("fishV2 name=%s age=%d length=%d weight=%d sleeping=%b hungry=%b angry=%b", fishV2.getName(), - fishV2.getAge(), fishV2.getLengthMeters(), fishV2.getWeightTons(), fishV2.getHungry(), fishV2.getAngry(), fishV2.getSleeping())); - RainbowFishSerializer.writeV2(fishV2, "fish2.out"); - // Read V2 with V1 method - RainbowFish deserializedFishV2 = RainbowFishSerializer.readV1("fish2.out"); - System.out.println(String.format("deserializedFishV2 name=%s age=%d length=%d weight=%d", deserializedFishV2.getName(), - deserializedFishV2.getAge(), deserializedFishV2.getLengthMeters(), deserializedFishV2.getWeightTons())); - } + + /** + * Program entry point + */ + public static void main(String[] args) throws IOException, ClassNotFoundException { + // Write V1 + RainbowFish fishV1 = new RainbowFish("Zed", 10, 11, 12); + System.out.println(String.format("fishV1 name=%s age=%d length=%d weight=%d", fishV1.getName(), + fishV1.getAge(), fishV1.getLengthMeters(), fishV1.getWeightTons())); + RainbowFishSerializer.writeV1(fishV1, "fish1.out"); + // Read V1 + RainbowFish deserializedFishV1 = RainbowFishSerializer.readV1("fish1.out"); + System.out.println(String.format("deserializedFishV1 name=%s age=%d length=%d weight=%d", + deserializedFishV1.getName(), deserializedFishV1.getAge(), + deserializedFishV1.getLengthMeters(), deserializedFishV1.getWeightTons())); + // Write V2 + RainbowFishV2 fishV2 = new RainbowFishV2("Scar", 5, 12, 15, true, true, true); + System.out.println(String.format( + "fishV2 name=%s age=%d length=%d weight=%d sleeping=%b hungry=%b angry=%b", + fishV2.getName(), fishV2.getAge(), fishV2.getLengthMeters(), fishV2.getWeightTons(), + fishV2.getHungry(), fishV2.getAngry(), fishV2.getSleeping())); + RainbowFishSerializer.writeV2(fishV2, "fish2.out"); + // Read V2 with V1 method + RainbowFish deserializedFishV2 = RainbowFishSerializer.readV1("fish2.out"); + System.out.println(String.format("deserializedFishV2 name=%s age=%d length=%d weight=%d", + deserializedFishV2.getName(), deserializedFishV2.getAge(), + deserializedFishV2.getLengthMeters(), deserializedFishV2.getWeightTons())); + } } diff --git a/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/RainbowFish.java b/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/RainbowFish.java index 5e1fb1832..1b1825a3e 100644 --- a/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/RainbowFish.java +++ b/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/RainbowFish.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.tolerantreader; import java.io.Serializable; @@ -9,34 +31,37 @@ import java.io.Serializable; */ public class RainbowFish implements Serializable { - private static final long serialVersionUID = 1L; - - private String name; - private int age; - private int lengthMeters; - private int weightTons; - - public RainbowFish(String name, int age, int lengthMeters, int weightTons) { - this.name = name; - this.age = age; - this.lengthMeters = lengthMeters; - this.weightTons = weightTons; - } - - public String getName() { - return name; - } + private static final long serialVersionUID = 1L; - public int getAge() { - return age; - } + private String name; + private int age; + private int lengthMeters; + private int weightTons; - public int getLengthMeters() { - return lengthMeters; - } + /** + * Constructor + */ + public RainbowFish(String name, int age, int lengthMeters, int weightTons) { + this.name = name; + this.age = age; + this.lengthMeters = lengthMeters; + this.weightTons = weightTons; + } - public int getWeightTons() { - return weightTons; - } + public String getName() { + return name; + } + + public int getAge() { + return age; + } + + public int getLengthMeters() { + return lengthMeters; + } + + public int getWeightTons() { + return weightTons; + } } diff --git a/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/RainbowFishSerializer.java b/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/RainbowFishSerializer.java index ec482a34a..948dfa6d2 100644 --- a/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/RainbowFishSerializer.java +++ b/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/RainbowFishSerializer.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.tolerantreader; import java.io.FileInputStream; @@ -10,71 +32,62 @@ import java.util.Map; /** * - * RainbowFishSerializer provides methods for reading and writing RainbowFish objects to file. - * Tolerant Reader pattern is implemented here by serializing maps instead of RainbowFish objects. - * This way the reader does not break even though new properties are added to the schema. + * RainbowFishSerializer provides methods for reading and writing {@link RainbowFish} objects to + * file. Tolerant Reader pattern is implemented here by serializing maps instead of + * {@link RainbowFish} objects. This way the reader does not break even though new properties are + * added to the schema. * */ -public class RainbowFishSerializer { +public final class RainbowFishSerializer { - /** - * Write V1 RainbowFish to file - * @param rainbowFish - * @param filename - * @throws IOException - */ - public static void writeV1(RainbowFish rainbowFish, String filename) throws IOException { - Map map = new HashMap<>(); - map.put("name", rainbowFish.getName()); - map.put("age", String.format("%d", rainbowFish.getAge())); - map.put("lengthMeters", String.format("%d", rainbowFish.getLengthMeters())); - map.put("weightTons", String.format("%d", rainbowFish.getWeightTons())); - FileOutputStream fileOut = new FileOutputStream(filename); - ObjectOutputStream objOut = new ObjectOutputStream(fileOut); - objOut.writeObject(map); - objOut.close(); - fileOut.close(); - } + private RainbowFishSerializer() { + } - /** - * Write V2 RainbowFish to file - * @param rainbowFish - * @param filename - * @throws IOException - */ - public static void writeV2(RainbowFishV2 rainbowFish, String filename) throws IOException { - Map map = new HashMap<>(); - map.put("name", rainbowFish.getName()); - map.put("age", String.format("%d", rainbowFish.getAge())); - map.put("lengthMeters", String.format("%d", rainbowFish.getLengthMeters())); - map.put("weightTons", String.format("%d", rainbowFish.getWeightTons())); - map.put("angry", Boolean.toString(rainbowFish.getAngry())); - map.put("hungry", Boolean.toString(rainbowFish.getHungry())); - map.put("sleeping", Boolean.toString(rainbowFish.getSleeping())); - FileOutputStream fileOut = new FileOutputStream(filename); - ObjectOutputStream objOut = new ObjectOutputStream(fileOut); - objOut.writeObject(map); - objOut.close(); - fileOut.close(); - } - - /** - * Read V1 RainbowFish from file - * @param filename - * @return - * @throws IOException - * @throws ClassNotFoundException - */ - public static RainbowFish readV1(String filename) throws IOException, ClassNotFoundException { - Map map = null; - FileInputStream fileIn = new FileInputStream(filename); - ObjectInputStream objIn = new ObjectInputStream(fileIn); - map = (Map) objIn.readObject(); - objIn.close(); - fileIn.close(); - return new RainbowFish(map.get("name"), - Integer.parseInt(map.get("age")), - Integer.parseInt(map.get("lengthMeters")), - Integer.parseInt(map.get("weightTons"))); - } + /** + * Write V1 RainbowFish to file + */ + public static void writeV1(RainbowFish rainbowFish, String filename) throws IOException { + Map map = new HashMap<>(); + map.put("name", rainbowFish.getName()); + map.put("age", String.format("%d", rainbowFish.getAge())); + map.put("lengthMeters", String.format("%d", rainbowFish.getLengthMeters())); + map.put("weightTons", String.format("%d", rainbowFish.getWeightTons())); + FileOutputStream fileOut = new FileOutputStream(filename); + ObjectOutputStream objOut = new ObjectOutputStream(fileOut); + objOut.writeObject(map); + objOut.close(); + fileOut.close(); + } + + /** + * Write V2 RainbowFish to file + */ + public static void writeV2(RainbowFishV2 rainbowFish, String filename) throws IOException { + Map map = new HashMap<>(); + map.put("name", rainbowFish.getName()); + map.put("age", String.format("%d", rainbowFish.getAge())); + map.put("lengthMeters", String.format("%d", rainbowFish.getLengthMeters())); + map.put("weightTons", String.format("%d", rainbowFish.getWeightTons())); + map.put("angry", Boolean.toString(rainbowFish.getAngry())); + map.put("hungry", Boolean.toString(rainbowFish.getHungry())); + map.put("sleeping", Boolean.toString(rainbowFish.getSleeping())); + FileOutputStream fileOut = new FileOutputStream(filename); + ObjectOutputStream objOut = new ObjectOutputStream(fileOut); + objOut.writeObject(map); + objOut.close(); + fileOut.close(); + } + + /** + * Read V1 RainbowFish from file + */ + public static RainbowFish readV1(String filename) throws IOException, ClassNotFoundException { + FileInputStream fileIn = new FileInputStream(filename); + ObjectInputStream objIn = new ObjectInputStream(fileIn); + Map map = (Map) objIn.readObject(); + objIn.close(); + fileIn.close(); + return new RainbowFish(map.get("name"), Integer.parseInt(map.get("age")), Integer.parseInt(map + .get("lengthMeters")), Integer.parseInt(map.get("weightTons"))); + } } diff --git a/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/RainbowFishV2.java b/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/RainbowFishV2.java index dfa08ed01..55a416734 100644 --- a/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/RainbowFishV2.java +++ b/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/RainbowFishV2.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.tolerantreader; /** @@ -7,32 +29,36 @@ package com.iluwatar.tolerantreader; */ public class RainbowFishV2 extends RainbowFish { - private static final long serialVersionUID = 1L; - - private boolean sleeping; - private boolean hungry; - private boolean angry; + private static final long serialVersionUID = 1L; - public RainbowFishV2(String name, int age, int lengthMeters, int weightTons) { - super(name, age, lengthMeters, weightTons); - } - - public RainbowFishV2(String name, int age, int lengthMeters, int weightTons, boolean sleeping, boolean hungry, boolean angry) { - this(name, age, lengthMeters, weightTons); - this.sleeping = sleeping; - this.hungry = hungry; - this.angry = angry; - } - - public boolean getSleeping() { - return sleeping; - } - - public boolean getHungry() { - return hungry; - } - - public boolean getAngry() { - return angry; - } + private boolean sleeping; + private boolean hungry; + private boolean angry; + + public RainbowFishV2(String name, int age, int lengthMeters, int weightTons) { + super(name, age, lengthMeters, weightTons); + } + + /** + * Constructor + */ + public RainbowFishV2(String name, int age, int lengthMeters, int weightTons, boolean sleeping, + boolean hungry, boolean angry) { + this(name, age, lengthMeters, weightTons); + this.sleeping = sleeping; + this.hungry = hungry; + this.angry = angry; + } + + public boolean getSleeping() { + return sleeping; + } + + public boolean getHungry() { + return hungry; + } + + public boolean getAngry() { + return angry; + } } diff --git a/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/AppTest.java b/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/AppTest.java index cba24ada9..e1a2ca5f9 100644 --- a/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/AppTest.java +++ b/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/AppTest.java @@ -1,29 +1,53 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.tolerantreader; -import java.io.File; -import java.io.IOException; - import org.junit.After; import org.junit.Before; import org.junit.Test; -import com.iluwatar.tolerantreader.App; - +import java.io.File; +import java.io.IOException; +/** + * + * Application test + * + */ public class AppTest { - - @Test - public void test() throws ClassNotFoundException, IOException { - String[] args = {}; - App.main(args); - } - - @Before - @After - public void cleanup() { - File file1 = new File("fish1.out"); - file1.delete(); - File file2 = new File("fish2.out"); - file2.delete(); - } + + @Test + public void test() throws ClassNotFoundException, IOException { + String[] args = {}; + App.main(args); + } + + @Before + @After + public void cleanup() { + File file1 = new File("fish1.out"); + file1.delete(); + File file2 = new File("fish2.out"); + file2.delete(); + } } diff --git a/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/RainbowFishSerializerTest.java b/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/RainbowFishSerializerTest.java new file mode 100644 index 000000000..1028e7bde --- /dev/null +++ b/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/RainbowFishSerializerTest.java @@ -0,0 +1,90 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.tolerantreader; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; + +/** + * Date: 12/30/15 - 18:39 PM + * + * @author Jeroen Meulemeester + */ +public class RainbowFishSerializerTest { + + /** + * Create a temporary folder, used to generate files in during this test + */ + @Rule + public final TemporaryFolder testFolder = new TemporaryFolder(); + + /** + * Rainbow fish version 1 used during the tests + */ + private static final RainbowFish V1 = new RainbowFish("version1", 1, 2, 3); + + /** + * Rainbow fish version 2 used during the tests + */ + private static final RainbowFishV2 V2 = new RainbowFishV2("version2", 4, 5, 6, true, false, true); + + /** + * Verify if a fish, written as version 1 can be read back as version 1 + */ + @Test + public void testWriteV1ReadV1() throws Exception { + final File outputFile = this.testFolder.newFile(); + RainbowFishSerializer.writeV1(V1, outputFile.getPath()); + + final RainbowFish fish = RainbowFishSerializer.readV1(outputFile.getPath()); + assertNotSame(V1, fish); + assertEquals(V1.getName(), fish.getName()); + assertEquals(V1.getAge(), fish.getAge()); + assertEquals(V1.getLengthMeters(), fish.getLengthMeters()); + assertEquals(V1.getWeightTons(), fish.getWeightTons()); + + } + + /** + * Verify if a fish, written as version 2 can be read back as version 1 + */ + @Test + public void testWriteV2ReadV1() throws Exception { + final File outputFile = this.testFolder.newFile(); + RainbowFishSerializer.writeV2(V2, outputFile.getPath()); + + final RainbowFish fish = RainbowFishSerializer.readV1(outputFile.getPath()); + assertNotSame(V2, fish); + assertEquals(V2.getName(), fish.getName()); + assertEquals(V2.getAge(), fish.getAge()); + assertEquals(V2.getLengthMeters(), fish.getLengthMeters()); + assertEquals(V2.getWeightTons(), fish.getWeightTons()); + } + +} \ No newline at end of file diff --git a/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/RainbowFishTest.java b/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/RainbowFishTest.java new file mode 100644 index 000000000..f9dcc5e6d --- /dev/null +++ b/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/RainbowFishTest.java @@ -0,0 +1,48 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.tolerantreader; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Date: 12/30/15 - 18:34 PM + * + * @author Jeroen Meulemeester + */ +public class RainbowFishTest { + + /** + * Verify if the getters of a {@link RainbowFish} return the expected values + */ + @Test + public void testValues() { + final RainbowFish fish = new RainbowFish("name", 1, 2, 3); + assertEquals("name", fish.getName()); + assertEquals(1, fish.getAge()); + assertEquals(2, fish.getLengthMeters()); + assertEquals(3, fish.getWeightTons()); + } + +} \ No newline at end of file diff --git a/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/RainbowFishV2Test.java b/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/RainbowFishV2Test.java new file mode 100644 index 000000000..680e3c4ed --- /dev/null +++ b/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/RainbowFishV2Test.java @@ -0,0 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.tolerantreader; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Date: 12/30/15 - 18:35 PM + * + * @author Jeroen Meulemeester + */ +public class RainbowFishV2Test { + + /** + * Verify if the getters of a {@link RainbowFish} return the expected values + */ + @Test + public void testValues() { + final RainbowFishV2 fish = new RainbowFishV2("name", 1, 2, 3, false, true, false); + assertEquals("name", fish.getName()); + assertEquals(1, fish.getAge()); + assertEquals(2, fish.getLengthMeters()); + assertEquals(3, fish.getWeightTons()); + assertEquals(false, fish.getSleeping()); + assertEquals(true, fish.getHungry()); + assertEquals(false, fish.getAngry()); + } + +} \ No newline at end of file diff --git a/twin/.gitignore b/twin/.gitignore new file mode 100644 index 000000000..b83d22266 --- /dev/null +++ b/twin/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/twin/README.md b/twin/README.md new file mode 100644 index 000000000..3795236bb --- /dev/null +++ b/twin/README.md @@ -0,0 +1,26 @@ +--- +layout: pattern +title: Twin +folder: twin +permalink: /patterns/twin/ +categories: Creational +tags: + - Java + - Difficulty-Intermediate +--- + +## Intent + Twin pattern is a design pattern which provides a standard solution to simulate multiple +inheritance in java + +![alt text](./etc/twin.png "Twin") + +## Applicability +Use the Twin idiom when + +* to simulate multiple inheritance in a language that does not support this feature. +* to avoid certain problems of multiple inheritance such as name clashes. + +## Credits + +* [Twin – A Design Pattern for Modeling Multiple Inheritance](http://www.ssw.uni-linz.ac.at/Research/Papers/Moe99/Paper.pdf) diff --git a/twin/etc/twin.png b/twin/etc/twin.png new file mode 100644 index 000000000..724092525 Binary files /dev/null and b/twin/etc/twin.png differ diff --git a/twin/etc/twin.ucls b/twin/etc/twin.ucls new file mode 100644 index 000000000..6948dbf6a --- /dev/null +++ b/twin/etc/twin.ucls @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/twin/pom.xml b/twin/pom.xml new file mode 100644 index 000000000..1eb854b87 --- /dev/null +++ b/twin/pom.xml @@ -0,0 +1,47 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + twin + + + junit + junit + test + + + org.mockito + mockito-core + test + + + diff --git a/twin/src/main/java/com/iluwatar/twin/App.java b/twin/src/main/java/com/iluwatar/twin/App.java new file mode 100644 index 000000000..95998df33 --- /dev/null +++ b/twin/src/main/java/com/iluwatar/twin/App.java @@ -0,0 +1,68 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.twin; + +/** + * Twin pattern is a design pattern which provides a standard solution to simulate multiple + * inheritance in java. + *

    + * In this example, the essence of the Twin pattern is the {@link BallItem} class and + * {@link BallThread} class represent the twin objects to coordinate with each other(via the twin + * reference) like a single class inheriting from {@link GameItem} and {@link Thread}. + */ + +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) throws Exception { + + BallItem ballItem = new BallItem(); + BallThread ballThread = new BallThread(); + + ballItem.setTwin(ballThread); + ballThread.setTwin(ballItem); + + ballThread.start(); + + waiting(); + + ballItem.click(); + + waiting(); + + ballItem.click(); + + waiting(); + + // exit + ballThread.stopMe(); + } + + private static void waiting() throws Exception { + Thread.sleep(750); + } +} diff --git a/twin/src/main/java/com/iluwatar/twin/BallItem.java b/twin/src/main/java/com/iluwatar/twin/BallItem.java new file mode 100644 index 000000000..0188b5731 --- /dev/null +++ b/twin/src/main/java/com/iluwatar/twin/BallItem.java @@ -0,0 +1,63 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.twin; + +/** + * This class represents a Ball which extends {@link GameItem} and implements the logic for ball + * item, like move and draw. It hold a reference of {@link BallThread} to delegate the suspend and + * resume task. + */ +public class BallItem extends GameItem { + + private boolean isSuspended; + + private BallThread twin; + + public void setTwin(BallThread twin) { + this.twin = twin; + } + + @Override + public void doDraw() { + + System.out.println("doDraw"); + } + + public void move() { + System.out.println("move"); + } + + @Override + public void click() { + + isSuspended = !isSuspended; + + if (isSuspended) { + twin.suspendMe(); + } else { + twin.resumeMe(); + } + } +} + diff --git a/twin/src/main/java/com/iluwatar/twin/BallThread.java b/twin/src/main/java/com/iluwatar/twin/BallThread.java new file mode 100644 index 000000000..194d85b06 --- /dev/null +++ b/twin/src/main/java/com/iluwatar/twin/BallThread.java @@ -0,0 +1,77 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.twin; + +/** + * This class is a UI thread for drawing the {@link BallItem}, and provide the method for suspend + * and resume. It hold the reference of {@link BallItem} to delegate the draw task. + * + */ + +public class BallThread extends Thread { + + private BallItem twin; + + private volatile boolean isSuspended; + + private volatile boolean isRunning = true; + + public void setTwin(BallItem twin) { + this.twin = twin; + } + + /** + * Run the thread + */ + public void run() { + + while (isRunning) { + if (!isSuspended) { + twin.draw(); + twin.move(); + } + try { + Thread.sleep(250); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + + public void suspendMe() { + isSuspended = true; + System.out.println("Begin to suspend BallThread"); + } + + public void resumeMe() { + isSuspended = false; + System.out.println("Begin to resume BallThread"); + } + + public void stopMe() { + this.isRunning = false; + this.isSuspended = true; + } +} + diff --git a/twin/src/main/java/com/iluwatar/twin/GameItem.java b/twin/src/main/java/com/iluwatar/twin/GameItem.java new file mode 100644 index 000000000..08d7dcce7 --- /dev/null +++ b/twin/src/main/java/com/iluwatar/twin/GameItem.java @@ -0,0 +1,44 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +package com.iluwatar.twin; + +/** + * GameItem is a common class which provides some common methods for game object. + */ +public abstract class GameItem { + + /** + * Template method, do some common logic before draw + */ + public void draw() { + System.out.println("draw"); + doDraw(); + } + + public abstract void doDraw(); + + + public abstract void click(); +} diff --git a/twin/src/test/java/com/iluwatar/twin/AppTest.java b/twin/src/test/java/com/iluwatar/twin/AppTest.java new file mode 100644 index 000000000..73a1d6322 --- /dev/null +++ b/twin/src/test/java/com/iluwatar/twin/AppTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.twin; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() throws Exception { + String[] args = {}; + App.main(args); + } +} diff --git a/twin/src/test/java/com/iluwatar/twin/BallItemTest.java b/twin/src/test/java/com/iluwatar/twin/BallItemTest.java new file mode 100644 index 000000000..4bb9a2111 --- /dev/null +++ b/twin/src/test/java/com/iluwatar/twin/BallItemTest.java @@ -0,0 +1,84 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.twin; + +import org.junit.Test; +import org.mockito.InOrder; + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/30/15 - 18:44 PM + * + * @author Jeroen Meulemeester + */ +public class BallItemTest extends StdOutTest { + + @Test + public void testClick() { + final BallThread ballThread = mock(BallThread.class); + final BallItem ballItem = new BallItem(); + ballItem.setTwin(ballThread); + + final InOrder inOrder = inOrder(ballThread); + + for (int i = 0; i < 10; i++) { + ballItem.click(); + inOrder.verify(ballThread).suspendMe(); + + ballItem.click(); + inOrder.verify(ballThread).resumeMe(); + } + + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testDoDraw() { + final BallItem ballItem = new BallItem(); + final BallThread ballThread = mock(BallThread.class); + ballItem.setTwin(ballThread); + + ballItem.draw(); + verify(getStdOutMock()).println("draw"); + verify(getStdOutMock()).println("doDraw"); + + verifyNoMoreInteractions(ballThread, getStdOutMock()); + } + + @Test + public void testMove() { + final BallItem ballItem = new BallItem(); + final BallThread ballThread = mock(BallThread.class); + ballItem.setTwin(ballThread); + + ballItem.move(); + verify(getStdOutMock()).println("move"); + + verifyNoMoreInteractions(ballThread, getStdOutMock()); + } + +} \ No newline at end of file diff --git a/twin/src/test/java/com/iluwatar/twin/BallThreadTest.java b/twin/src/test/java/com/iluwatar/twin/BallThreadTest.java new file mode 100644 index 000000000..453109e5a --- /dev/null +++ b/twin/src/test/java/com/iluwatar/twin/BallThreadTest.java @@ -0,0 +1,111 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.twin; + +import org.junit.Test; + +import static java.lang.Thread.UncaughtExceptionHandler; +import static java.lang.Thread.sleep; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/30/15 - 18:55 PM + * + * @author Jeroen Meulemeester + */ +public class BallThreadTest { + + /** + * Verify if the {@link BallThread} can be resumed + */ + @Test(timeout = 5000) + public void testSuspend() throws Exception { + final BallThread ballThread = new BallThread(); + + final BallItem ballItem = mock(BallItem.class); + ballThread.setTwin(ballItem); + + ballThread.start(); + + verify(ballItem, timeout(2000).atLeastOnce()).draw(); + verify(ballItem, timeout(2000).atLeastOnce()).move(); + ballThread.suspendMe(); + + sleep(1000); + + ballThread.stopMe(); + ballThread.join(); + + verifyNoMoreInteractions(ballItem); + } + + /** + * Verify if the {@link BallThread} can be resumed + */ + @Test(timeout = 5000) + public void testResume() throws Exception { + final BallThread ballThread = new BallThread(); + + final BallItem ballItem = mock(BallItem.class); + ballThread.setTwin(ballItem); + + ballThread.suspendMe(); + ballThread.start(); + + sleep(1000); + + verifyZeroInteractions(ballItem); + + ballThread.resumeMe(); + verify(ballItem, timeout(2000).atLeastOnce()).draw(); + verify(ballItem, timeout(2000).atLeastOnce()).move(); + + ballThread.stopMe(); + ballThread.join(); + + verifyNoMoreInteractions(ballItem); + } + + /** + * Verify if the {@link BallThread} is interruptible + */ + @Test(timeout = 5000) + public void testInterrupt() throws Exception { + final BallThread ballThread = new BallThread(); + final UncaughtExceptionHandler exceptionHandler = mock(UncaughtExceptionHandler.class); + ballThread.setUncaughtExceptionHandler(exceptionHandler); + ballThread.setTwin(mock(BallItem.class)); + ballThread.start(); + ballThread.interrupt(); + ballThread.join(); + + verify(exceptionHandler).uncaughtException(eq(ballThread), any(RuntimeException.class)); + verifyNoMoreInteractions(exceptionHandler); + } +} \ No newline at end of file diff --git a/twin/src/test/java/com/iluwatar/twin/StdOutTest.java b/twin/src/test/java/com/iluwatar/twin/StdOutTest.java new file mode 100644 index 000000000..b3baf8abd --- /dev/null +++ b/twin/src/test/java/com/iluwatar/twin/StdOutTest.java @@ -0,0 +1,75 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.twin; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since some actions don't have any + * influence on accessible objects, except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/update-ghpages.sh b/update-ghpages.sh new file mode 100644 index 000000000..aee888ba9 --- /dev/null +++ b/update-ghpages.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# +# The MIT License +# Copyright (c) 2014 Ilkka Seppälä +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +# Clone gh-pages +git clone -b gh-pages "https://${GH_REF}" ghpagesclone +cd ghpagesclone + +# Init and update submodule to latest +git submodule update --init --recursive +git submodule update --remote + +# Setup Git +git config user.name "Travis-CI" +git config user.email "travis@no.reply" + +# If there is a new version of the master branch +if git status | grep patterns > /dev/null 2>&1 +then + # it should be committed + git add . + git commit -m ":sparkles: :up: Automagic Update via Travis-CI" + git push --quiet "https://${GH_TOKEN}:x-oauth-basic@${GH_REF}" gh-pages > /dev/null 2>&1 +fi diff --git a/value-object/README.md b/value-object/README.md new file mode 100644 index 000000000..83223d8a2 --- /dev/null +++ b/value-object/README.md @@ -0,0 +1,34 @@ +--- +layout: pattern +title: Value Object +folder: value-object +permalink: /patterns/value-object/ +categories: Creational +tags: + - Java + - Difficulty-Beginner +--- + +## Intent +Provide objects which follow value semantics rather than reference semantics. +This means value objects' equality are not based on identity. Two value objects are +equal when they have the same value, not necessarily being the same object. + +![alt text](./etc/value-object.png "Value Object") + +## Applicability +Use the Value Object when + +* you need to measure the objects' equality based on the objects' value + +## Real world examples + +* [java.util.Optional](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html) +* [java.time.LocalDate](https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html) +* [joda-time, money, beans](http://www.joda.org/) + +## Credits + +* [Patterns of Enterprise Application Architecture](http://www.martinfowler.com/books/eaa.html) +* [VALJOs - Value Java Objects : Stephen Colebourne's blog](http://blog.joda.org/2014/03/valjos-value-java-objects.html) +* [Value Object : Wikipedia](https://en.wikipedia.org/wiki/Value_object) diff --git a/value-object/etc/value-object.png b/value-object/etc/value-object.png new file mode 100644 index 000000000..69a244c80 Binary files /dev/null and b/value-object/etc/value-object.png differ diff --git a/value-object/etc/value-object.ucls b/value-object/etc/value-object.ucls new file mode 100644 index 000000000..7bbf5e47c --- /dev/null +++ b/value-object/etc/value-object.ucls @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/value-object/pom.xml b/value-object/pom.xml new file mode 100644 index 000000000..ec8de1681 --- /dev/null +++ b/value-object/pom.xml @@ -0,0 +1,54 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + value-object + + + com.google.guava + guava-testlib + 19.0 + test + + + junit + junit + test + + + org.mockito + mockito-core + test + + + diff --git a/value-object/src/main/java/com/iluwatar/value/object/App.java b/value-object/src/main/java/com/iluwatar/value/object/App.java new file mode 100644 index 000000000..1e943d054 --- /dev/null +++ b/value-object/src/main/java/com/iluwatar/value/object/App.java @@ -0,0 +1,54 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.value.object; + +/** + * A Value Object are objects which follow value semantics rather than reference semantics. This + * means value objects' equality are not based on identity. Two value objects are equal when they + * have the same value, not necessarily being the same object.. + * + * Value Objects must override equals(), hashCode() to check the equality with values. + * Value Objects should be immutable so declare members final. + * Obtain instances by static factory methods. + * The elements of the state must be other values, including primitive types. + * Provide methods, typically simple getters, to get the elements of the state. + * A Value Object must check equality with equals() not == + * + * For more specific and strict rules to implement value objects check the rules from Stephen + * Colebourne's term VALJO : http://blog.joda.org/2014/03/valjos-value-java-objects.html + */ +public class App { + /** + * This practice creates three HeroStats(Value object) and checks equality between those. + */ + public static void main(String[] args) { + HeroStat statA = HeroStat.valueOf(10, 5, 0); + HeroStat statB = HeroStat.valueOf(10, 5, 0); + HeroStat statC = HeroStat.valueOf(5, 1, 8); + + System.out.println(statA.toString()); + + System.out.println("Is statA and statB equal : " + statA.equals(statB)); + System.out.println("Is statA and statC equal : " + statA.equals(statC)); + } +} diff --git a/value-object/src/main/java/com/iluwatar/value/object/HeroStat.java b/value-object/src/main/java/com/iluwatar/value/object/HeroStat.java new file mode 100644 index 000000000..258c4d6a0 --- /dev/null +++ b/value-object/src/main/java/com/iluwatar/value/object/HeroStat.java @@ -0,0 +1,112 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.value.object; + +/** + * HeroStat is a value object + * + * {@link http://docs.oracle.com/javase/8/docs/api/java/lang/doc-files/ValueBased.html} + */ +public class HeroStat { + + // Stats for a hero + + private final int strength; + private final int intelligence; + private final int luck; + + // All constructors must be private. + private HeroStat(int strength, int intelligence, int luck) { + super(); + this.strength = strength; + this.intelligence = intelligence; + this.luck = luck; + } + + // Static factory method to create new instances. + public static HeroStat valueOf(int strength, int intelligence, int luck) { + return new HeroStat(strength, intelligence, luck); + } + + public int getStrength() { + return strength; + } + + public int getIntelligence() { + return intelligence; + } + + public int getLuck() { + return luck; + } + + /* + * Recommended to provide a static factory method capable of creating an instance from the formal + * string representation declared like this. public static HeroStat parse(String string) {} + */ + + // toString, hashCode, equals + + @Override + public String toString() { + return "HeroStat [strength=" + strength + ", intelligence=" + intelligence + + ", luck=" + luck + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + intelligence; + result = prime * result + luck; + result = prime * result + strength; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + HeroStat other = (HeroStat) obj; + if (intelligence != other.intelligence) { + return false; + } + if (luck != other.luck) { + return false; + } + if (strength != other.strength) { + return false; + } + return true; + } + + // The clone() method should not be public. Just don't override it. + +} diff --git a/value-object/src/test/java/com/iluwatar/value/object/AppTest.java b/value-object/src/test/java/com/iluwatar/value/object/AppTest.java new file mode 100644 index 000000000..85ef8b84e --- /dev/null +++ b/value-object/src/test/java/com/iluwatar/value/object/AppTest.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.value.object; + +import org.junit.Test; + +/** + * Application test + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/value-object/src/test/java/com/iluwatar/value/object/HeroStatTest.java b/value-object/src/test/java/com/iluwatar/value/object/HeroStatTest.java new file mode 100644 index 000000000..4a8034b0b --- /dev/null +++ b/value-object/src/test/java/com/iluwatar/value/object/HeroStatTest.java @@ -0,0 +1,66 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.value.object; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; + +import static org.junit.Assert.assertThat; + +import com.google.common.testing.EqualsTester; + +import org.junit.Test; + +/** + * Unit test for HeroStat. + */ +public class HeroStatTest { + + /** + * Tester for equals() and hashCode() methods of a class. Using guava's EqualsTester. + * + * @see http://static.javadoc.io/com.google.guava/guava-testlib/19.0/com/google/common/testing/ + * EqualsTester.html + */ + @Test + public void testEquals() { + HeroStat heroStatA = HeroStat.valueOf(3, 9, 2); + HeroStat heroStatB = HeroStat.valueOf(3, 9, 2); + new EqualsTester().addEqualityGroup(heroStatA, heroStatB).testEquals(); + } + + /** + * The toString() for two equal values must be the same. For two non-equal values it must be + * different. + */ + @Test + public void testToString() { + HeroStat heroStatA = HeroStat.valueOf(3, 9, 2); + HeroStat heroStatB = HeroStat.valueOf(3, 9, 2); + HeroStat heroStatC = HeroStat.valueOf(3, 9, 8); + + assertThat(heroStatA.toString(), is(heroStatB.toString())); + assertThat(heroStatA.toString(), is(not(heroStatC.toString()))); + } + +} diff --git a/visitor/README.md b/visitor/README.md new file mode 100644 index 000000000..c1e24a624 --- /dev/null +++ b/visitor/README.md @@ -0,0 +1,33 @@ +--- +layout: pattern +title: Visitor +folder: visitor +permalink: /patterns/visitor/ +categories: Behavioral +tags: + - Java + - Difficulty-Intermediate + - Gang Of Four +--- + +## Intent +Represent an operation to be performed on the elements of an object +structure. Visitor lets you define a new operation without changing the classes +of the elements on which it operates. + +![alt text](./etc/visitor_1.png "Visitor") + +## Applicability +Use the Visitor pattern when + +* an object structure contains many classes of objects with differing interfaces, and you want to perform operations on these objects that depend on their concrete classes +* many distinct and unrelated operations need to be performed on objects in an object structure, and you want to avoid "polluting" their classes with these operations. Visitor lets you keep related operations together by defining them in one class. When the object structure is shared by many applications, use Visitor to put operations in just those applications that need them +* the classes defining the object structure rarely change, but you often want to define new operations over the structure. Changing the object structure classes requires redefining the interface to all visitors, which is potentially costly. If the object structure classes change often, then it's probably better to define the operations in those classes + +## Real world examples + +* [Apache Wicket](https://github.com/apache/wicket) component tree, see [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java) + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/visitor/pom.xml b/visitor/pom.xml index 609892888..54a90e184 100644 --- a/visitor/pom.xml +++ b/visitor/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.1.0 + 1.13.0-SNAPSHOT visitor @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/visitor/src/main/java/com/iluwatar/visitor/App.java b/visitor/src/main/java/com/iluwatar/visitor/App.java index d9338777c..371756b84 100644 --- a/visitor/src/main/java/com/iluwatar/visitor/App.java +++ b/visitor/src/main/java/com/iluwatar/visitor/App.java @@ -1,27 +1,52 @@ -package com.iluwatar.visitor; - -/** - * - * Visitor pattern defines mechanism to apply operations on nodes - * in hierarchy. New operations can be added without altering the node - * interface. - * - * In this example there is a unit hierarchy beginning from Commander. - * This hierarchy is traversed by visitors. SoldierVisitor applies - * its operation on Soldiers, SergeantVisitor on Sergeants and so - * on. - * - */ -public class App { - - public static void main(String[] args) { - - Commander commander = new Commander(new Sergeant(new Soldier(), - new Soldier(), new Soldier()), new Sergeant(new Soldier(), - new Soldier(), new Soldier())); - commander.accept(new SoldierVisitor()); - commander.accept(new SergeantVisitor()); - commander.accept(new CommanderVisitor()); - - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.visitor; + +/** + * + * Visitor pattern defines mechanism to apply operations on nodes in hierarchy. New operations can + * be added without altering the node interface. + *

    + * In this example there is a unit hierarchy beginning from {@link Commander}. This hierarchy is + * traversed by visitors. {@link SoldierVisitor} applies its operation on {@link Soldier}s, + * {@link SergeantVisitor} on {@link Sergeant}s and so on. + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + + Commander commander = + new Commander(new Sergeant(new Soldier(), new Soldier(), new Soldier()), new Sergeant( + new Soldier(), new Soldier(), new Soldier())); + commander.accept(new SoldierVisitor()); + commander.accept(new SergeantVisitor()); + commander.accept(new CommanderVisitor()); + + } +} diff --git a/visitor/src/main/java/com/iluwatar/visitor/Commander.java b/visitor/src/main/java/com/iluwatar/visitor/Commander.java index 18f418b01..a1969a41c 100644 --- a/visitor/src/main/java/com/iluwatar/visitor/Commander.java +++ b/visitor/src/main/java/com/iluwatar/visitor/Commander.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.visitor; /** @@ -7,18 +29,18 @@ package com.iluwatar.visitor; */ public class Commander extends Unit { - public Commander(Unit... children) { - super(children); - } + public Commander(Unit... children) { + super(children); + } - @Override - public void accept(UnitVisitor visitor) { - visitor.visitCommander(this); - super.accept(visitor); - } + @Override + public void accept(UnitVisitor visitor) { + visitor.visitCommander(this); + super.accept(visitor); + } - @Override - public String toString() { - return "commander"; - } + @Override + public String toString() { + return "commander"; + } } diff --git a/visitor/src/main/java/com/iluwatar/visitor/CommanderVisitor.java b/visitor/src/main/java/com/iluwatar/visitor/CommanderVisitor.java index 71e127d14..6e54c7861 100644 --- a/visitor/src/main/java/com/iluwatar/visitor/CommanderVisitor.java +++ b/visitor/src/main/java/com/iluwatar/visitor/CommanderVisitor.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.visitor; /** @@ -7,17 +29,18 @@ package com.iluwatar.visitor; */ public class CommanderVisitor implements UnitVisitor { - @Override - public void visitSoldier(Soldier soldier) { - } + @Override + public void visitSoldier(Soldier soldier) { + // Do nothing + } - @Override - public void visitSergeant(Sergeant sergeant) { - } - - @Override - public void visitCommander(Commander commander) { - System.out.println("Good to see you " + commander); - } + @Override + public void visitSergeant(Sergeant sergeant) { + // Do nothing + } + @Override + public void visitCommander(Commander commander) { + System.out.println("Good to see you " + commander); + } } diff --git a/visitor/src/main/java/com/iluwatar/visitor/Sergeant.java b/visitor/src/main/java/com/iluwatar/visitor/Sergeant.java index 17a19780a..3b0087582 100644 --- a/visitor/src/main/java/com/iluwatar/visitor/Sergeant.java +++ b/visitor/src/main/java/com/iluwatar/visitor/Sergeant.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.visitor; /** @@ -7,18 +29,18 @@ package com.iluwatar.visitor; */ public class Sergeant extends Unit { - public Sergeant(Unit... children) { - super(children); - } + public Sergeant(Unit... children) { + super(children); + } - @Override - public void accept(UnitVisitor visitor) { - visitor.visitSergeant(this); - super.accept(visitor); - } + @Override + public void accept(UnitVisitor visitor) { + visitor.visitSergeant(this); + super.accept(visitor); + } - @Override - public String toString() { - return "sergeant"; - } + @Override + public String toString() { + return "sergeant"; + } } diff --git a/visitor/src/main/java/com/iluwatar/visitor/SergeantVisitor.java b/visitor/src/main/java/com/iluwatar/visitor/SergeantVisitor.java index a1b9165eb..4fca0a5bd 100644 --- a/visitor/src/main/java/com/iluwatar/visitor/SergeantVisitor.java +++ b/visitor/src/main/java/com/iluwatar/visitor/SergeantVisitor.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.visitor; /** @@ -7,17 +29,18 @@ package com.iluwatar.visitor; */ public class SergeantVisitor implements UnitVisitor { - @Override - public void visitSoldier(Soldier soldier) { - } + @Override + public void visitSoldier(Soldier soldier) { + // Do nothing + } - @Override - public void visitSergeant(Sergeant sergeant) { - System.out.println("Hello " + sergeant); - } - - @Override - public void visitCommander(Commander commander) { - } + @Override + public void visitSergeant(Sergeant sergeant) { + System.out.println("Hello " + sergeant); + } + @Override + public void visitCommander(Commander commander) { + // Do nothing + } } diff --git a/visitor/src/main/java/com/iluwatar/visitor/Soldier.java b/visitor/src/main/java/com/iluwatar/visitor/Soldier.java index f71fe5bc0..6e82a936b 100644 --- a/visitor/src/main/java/com/iluwatar/visitor/Soldier.java +++ b/visitor/src/main/java/com/iluwatar/visitor/Soldier.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.visitor; /** @@ -7,18 +29,18 @@ package com.iluwatar.visitor; */ public class Soldier extends Unit { - public Soldier(Unit... children) { - super(children); - } + public Soldier(Unit... children) { + super(children); + } - @Override - public void accept(UnitVisitor visitor) { - visitor.visitSoldier(this); - super.accept(visitor); - } + @Override + public void accept(UnitVisitor visitor) { + visitor.visitSoldier(this); + super.accept(visitor); + } - @Override - public String toString() { - return "soldier"; - } + @Override + public String toString() { + return "soldier"; + } } diff --git a/visitor/src/main/java/com/iluwatar/visitor/SoldierVisitor.java b/visitor/src/main/java/com/iluwatar/visitor/SoldierVisitor.java index 828212dee..fff24f699 100644 --- a/visitor/src/main/java/com/iluwatar/visitor/SoldierVisitor.java +++ b/visitor/src/main/java/com/iluwatar/visitor/SoldierVisitor.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.visitor; /** @@ -7,17 +29,18 @@ package com.iluwatar.visitor; */ public class SoldierVisitor implements UnitVisitor { - @Override - public void visitSoldier(Soldier soldier) { - System.out.println("Greetings " + soldier); - } + @Override + public void visitSoldier(Soldier soldier) { + System.out.println("Greetings " + soldier); + } - @Override - public void visitSergeant(Sergeant sergeant) { - } - - @Override - public void visitCommander(Commander commander) { - } + @Override + public void visitSergeant(Sergeant sergeant) { + // Do nothing + } + @Override + public void visitCommander(Commander commander) { + // Do nothing + } } diff --git a/visitor/src/main/java/com/iluwatar/visitor/Unit.java b/visitor/src/main/java/com/iluwatar/visitor/Unit.java index fbf1faae1..dc8bf2a28 100644 --- a/visitor/src/main/java/com/iluwatar/visitor/Unit.java +++ b/visitor/src/main/java/com/iluwatar/visitor/Unit.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.visitor; /** @@ -7,15 +29,18 @@ package com.iluwatar.visitor; */ public abstract class Unit { - private Unit[] children; + private Unit[] children; - public Unit(Unit... children) { - this.children = children; - } + public Unit(Unit... children) { + this.children = children; + } - public void accept(UnitVisitor visitor) { - for (Unit child : children) { - child.accept(visitor); - } - } + /** + * Accept visitor + */ + public void accept(UnitVisitor visitor) { + for (Unit child : children) { + child.accept(visitor); + } + } } diff --git a/visitor/src/main/java/com/iluwatar/visitor/UnitVisitor.java b/visitor/src/main/java/com/iluwatar/visitor/UnitVisitor.java index b0148bc7e..e465a6473 100644 --- a/visitor/src/main/java/com/iluwatar/visitor/UnitVisitor.java +++ b/visitor/src/main/java/com/iluwatar/visitor/UnitVisitor.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.visitor; /** @@ -7,10 +29,10 @@ package com.iluwatar.visitor; */ public interface UnitVisitor { - void visitSoldier(Soldier soldier); + void visitSoldier(Soldier soldier); - void visitSergeant(Sergeant sergeant); + void visitSergeant(Sergeant sergeant); - void visitCommander(Commander commander); + void visitCommander(Commander commander); } diff --git a/visitor/src/test/java/com/iluwatar/visitor/AppTest.java b/visitor/src/test/java/com/iluwatar/visitor/AppTest.java index 5ba48c6c9..33674053e 100644 --- a/visitor/src/test/java/com/iluwatar/visitor/AppTest.java +++ b/visitor/src/test/java/com/iluwatar/visitor/AppTest.java @@ -1,14 +1,39 @@ -package com.iluwatar.visitor; - -import org.junit.Test; - -import com.iluwatar.visitor.App; - -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.visitor; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/visitor/src/test/java/com/iluwatar/visitor/CommanderTest.java b/visitor/src/test/java/com/iluwatar/visitor/CommanderTest.java new file mode 100644 index 000000000..abf92765f --- /dev/null +++ b/visitor/src/test/java/com/iluwatar/visitor/CommanderTest.java @@ -0,0 +1,47 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.visitor; + +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; + +/** + * Date: 12/30/15 - 19:45 PM + * + * @author Jeroen Meulemeester + */ +public class CommanderTest extends UnitTest { + + /** + * Create a new test instance for the given {@link Commander} + */ + public CommanderTest() { + super(Commander::new); + } + + @Override + void verifyVisit(Commander unit, UnitVisitor mockedVisitor) { + verify(mockedVisitor).visitCommander(eq(unit)); + } + +} \ No newline at end of file diff --git a/visitor/src/test/java/com/iluwatar/visitor/CommanderVisitorTest.java b/visitor/src/test/java/com/iluwatar/visitor/CommanderVisitorTest.java new file mode 100644 index 000000000..1331d25a9 --- /dev/null +++ b/visitor/src/test/java/com/iluwatar/visitor/CommanderVisitorTest.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.visitor; + +import java.util.Optional; + +/** + * Date: 12/30/15 - 18:43 PM + * + * @author Jeroen Meulemeester + */ +public class CommanderVisitorTest extends VisitorTest { + + /** + * Create a new test instance for the given visitor + */ + public CommanderVisitorTest() { + super( + new CommanderVisitor(), + Optional.of("Good to see you commander"), + Optional.empty(), + Optional.empty() + ); + } + +} diff --git a/visitor/src/test/java/com/iluwatar/visitor/SergeantTest.java b/visitor/src/test/java/com/iluwatar/visitor/SergeantTest.java new file mode 100644 index 000000000..fdc93265d --- /dev/null +++ b/visitor/src/test/java/com/iluwatar/visitor/SergeantTest.java @@ -0,0 +1,47 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.visitor; + +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; + +/** + * Date: 12/30/15 - 19:45 PM + * + * @author Jeroen Meulemeester + */ +public class SergeantTest extends UnitTest { + + /** + * Create a new test instance for the given {@link Sergeant} + */ + public SergeantTest() { + super(Sergeant::new); + } + + @Override + void verifyVisit(Sergeant unit, UnitVisitor mockedVisitor) { + verify(mockedVisitor).visitSergeant(eq(unit)); + } + +} \ No newline at end of file diff --git a/visitor/src/test/java/com/iluwatar/visitor/SergeantVisitorTest.java b/visitor/src/test/java/com/iluwatar/visitor/SergeantVisitorTest.java new file mode 100644 index 000000000..dc84d0b54 --- /dev/null +++ b/visitor/src/test/java/com/iluwatar/visitor/SergeantVisitorTest.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.visitor; + +import java.util.Optional; + +/** + * Date: 12/30/15 - 18:36 PM + * + * @author Jeroen Meulemeester + */ +public class SergeantVisitorTest extends VisitorTest { + + /** + * Create a new test instance for the given visitor + */ + public SergeantVisitorTest() { + super( + new SergeantVisitor(), + Optional.empty(), + Optional.of("Hello sergeant"), + Optional.empty() + ); + } + +} diff --git a/visitor/src/test/java/com/iluwatar/visitor/SoldierTest.java b/visitor/src/test/java/com/iluwatar/visitor/SoldierTest.java new file mode 100644 index 000000000..18b2f2c08 --- /dev/null +++ b/visitor/src/test/java/com/iluwatar/visitor/SoldierTest.java @@ -0,0 +1,47 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.visitor; + +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; + +/** + * Date: 12/30/15 - 19:45 PM + * + * @author Jeroen Meulemeester + */ +public class SoldierTest extends UnitTest { + + /** + * Create a new test instance for the given {@link Soldier} + */ + public SoldierTest() { + super(Soldier::new); + } + + @Override + void verifyVisit(Soldier unit, UnitVisitor mockedVisitor) { + verify(mockedVisitor).visitSoldier(eq(unit)); + } + +} \ No newline at end of file diff --git a/visitor/src/test/java/com/iluwatar/visitor/SoldierVisitorTest.java b/visitor/src/test/java/com/iluwatar/visitor/SoldierVisitorTest.java new file mode 100644 index 000000000..c56485eb4 --- /dev/null +++ b/visitor/src/test/java/com/iluwatar/visitor/SoldierVisitorTest.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.visitor; + +import java.util.Optional; + +/** + * Date: 12/30/15 - 18:59 PM + * + * @author Jeroen Meulemeester + */ +public class SoldierVisitorTest extends VisitorTest { + + /** + * Create a new test instance for the given visitor + */ + public SoldierVisitorTest() { + super( + new SoldierVisitor(), + Optional.empty(), + Optional.empty(), + Optional.of("Greetings soldier") + ); + } + +} diff --git a/visitor/src/test/java/com/iluwatar/visitor/StdOutTest.java b/visitor/src/test/java/com/iluwatar/visitor/StdOutTest.java new file mode 100644 index 000000000..075f235f5 --- /dev/null +++ b/visitor/src/test/java/com/iluwatar/visitor/StdOutTest.java @@ -0,0 +1,75 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.visitor; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since some actions don't have any + * influence on accessible objects, except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/visitor/src/test/java/com/iluwatar/visitor/UnitTest.java b/visitor/src/test/java/com/iluwatar/visitor/UnitTest.java new file mode 100644 index 000000000..cba91a7f6 --- /dev/null +++ b/visitor/src/test/java/com/iluwatar/visitor/UnitTest.java @@ -0,0 +1,82 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.visitor; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.function.Function; + +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/30/15 - 18:59 PM + * + * @author Jeroen Meulemeester + */ +public abstract class UnitTest { + + /** + * Factory to create new instances of the tested unit + */ + private final Function factory; + + /** + * Create a new test instance for the given unit type {@link U} + * + * @param factory Factory to create new instances of the tested unit + */ + public UnitTest(final Function factory) { + this.factory = factory; + } + + @Test + public void testAccept() throws Exception { + final Unit[] children = new Unit[5]; + Arrays.setAll(children, (i) -> mock(Unit.class)); + + final U unit = this.factory.apply(children); + final UnitVisitor visitor = mock(UnitVisitor.class); + unit.accept(visitor); + verifyVisit(unit, visitor); + + for (final Unit child : children) { + verify(child).accept(eq(visitor)); + } + + verifyNoMoreInteractions(children); + verifyNoMoreInteractions(visitor); + } + + /** + * Verify if the correct visit method is called on the mock, depending on the tested instance + * + * @param unit The tested unit instance + * @param mockedVisitor The mocked {@link UnitVisitor} who should have gotten a visit by the unit + */ + abstract void verifyVisit(final U unit, final UnitVisitor mockedVisitor); + +} \ No newline at end of file diff --git a/visitor/src/test/java/com/iluwatar/visitor/VisitorTest.java b/visitor/src/test/java/com/iluwatar/visitor/VisitorTest.java new file mode 100644 index 000000000..4a131bbf2 --- /dev/null +++ b/visitor/src/test/java/com/iluwatar/visitor/VisitorTest.java @@ -0,0 +1,102 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.visitor; + +import org.junit.Test; + +import java.util.Optional; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/30/15 - 18:59 PM + * + * @author Jeroen Meulemeester + */ +public abstract class VisitorTest extends StdOutTest { + + /** + * The tested visitor instance + */ + private final V visitor; + + /** + * The optional expected response when being visited by a commander + */ + private final Optional commanderResponse; + + /** + * The optional expected response when being visited by a sergeant + */ + private final Optional sergeantResponse; + + /** + * The optional expected response when being visited by a soldier + */ + private final Optional soldierResponse; + + /** + * Create a new test instance for the given visitor + * + * @param commanderResponse The optional expected response when being visited by a commander + * @param sergeantResponse The optional expected response when being visited by a sergeant + * @param soldierResponse The optional expected response when being visited by a soldier + */ + public VisitorTest(final V visitor, final Optional commanderResponse, + final Optional sergeantResponse, final Optional soldierResponse) { + + this.visitor = visitor; + this.commanderResponse = commanderResponse; + this.sergeantResponse = sergeantResponse; + this.soldierResponse = soldierResponse; + } + + @Test + public void testVisitCommander() { + this.visitor.visitCommander(new Commander()); + if (this.commanderResponse.isPresent()) { + verify(getStdOutMock()).println(this.commanderResponse.get()); + } + verifyNoMoreInteractions(getStdOutMock()); + } + + @Test + public void testVisitSergeant() { + this.visitor.visitSergeant(new Sergeant()); + if (this.sergeantResponse.isPresent()) { + verify(getStdOutMock()).println(this.sergeantResponse.get()); + } + verifyNoMoreInteractions(getStdOutMock()); + } + + @Test + public void testVisitSoldier() { + this.visitor.visitSoldier(new Soldier()); + if (this.soldierResponse.isPresent()) { + verify(getStdOutMock()).println(this.soldierResponse.get()); + } + verifyNoMoreInteractions(getStdOutMock()); + } + +}