Merge branch 'layers'

This commit is contained in:
Ilkka Seppala 2015-08-15 23:42:42 +03:00
commit 69fdf6858f
22 changed files with 1074 additions and 0 deletions

BIN
layers/etc/layers.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

256
layers/etc/layers.ucls Normal file
View File

@ -0,0 +1,256 @@
<?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
realizations="true" associations="true" dependencies="false" nesting-relationships="true">
<interface id="1" language="java" name="com.iluwatar.layers.CakeDao" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/CakeDao.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="289" y="916"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</interface>
<class id="2" language="java" name="com.iluwatar.layers.CakeLayer" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/CakeLayer.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="1438" y="826"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="3" language="java" name="com.iluwatar.layers.CakeViewImpl" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/CakeViewImpl.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="456" y="221"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="4" language="java" name="com.iluwatar.layers.CakeBakingException" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/CakeBakingException.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="143" y="502"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="5" language="java" name="com.iluwatar.layers.CakeBakingServiceImpl" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/CakeBakingServiceImpl.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="456" y="694"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<interface id="6" language="java" name="com.iluwatar.layers.CakeLayerDao" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/CakeLayerDao.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="456" y="918"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</interface>
<interface id="7" language="java" name="com.iluwatar.layers.View" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/View.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="456" y="65"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</interface>
<class id="8" language="java" name="com.iluwatar.layers.CakeToppingInfo" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/CakeToppingInfo.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="817" y="530"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="9" language="java" name="com.iluwatar.layers.CakeInfo" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/CakeInfo.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="883" y="265"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<interface id="10" language="java" name="com.iluwatar.layers.CakeToppingDao" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/CakeToppingDao.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="633" y="918"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</interface>
<interface id="11" language="java" name="com.iluwatar.layers.CakeBakingService" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/CakeBakingService.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="456" y="431"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</interface>
<class id="12" language="java" name="com.iluwatar.layers.CakeLayerInfo" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/CakeLayerInfo.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="1055" y="530"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="13" language="java" name="com.iluwatar.layers.Cake" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/Cake.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="1160" y="826"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="14" language="java" name="com.iluwatar.layers.CakeTopping" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/CakeTopping.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="876" y="826"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<association id="15">
<end type="SOURCE" refId="9" navigable="false">
<attribute id="16" name="cakeToppingInfo"/>
<multiplicity id="17" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="8" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<dependency id="18">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="10"/>
</dependency>
<association id="19">
<end type="SOURCE" refId="13" navigable="false">
<attribute id="20" name="layers"/>
<multiplicity id="21" minimum="0" maximum="2147483647"/>
</end>
<end type="TARGET" refId="2" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<dependency id="22">
<end type="SOURCE" refId="11"/>
<end type="TARGET" refId="4"/>
</dependency>
<realization id="23">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="11"/>
</realization>
<association id="24">
<end type="SOURCE" refId="13" navigable="false">
<attribute id="25" name="topping"/>
<multiplicity id="26" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="14" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<association id="27">
<end type="SOURCE" refId="2" navigable="false">
<attribute id="28" name="cake"/>
<multiplicity id="29" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="13" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<dependency id="30">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="14"/>
</dependency>
<dependency id="31">
<end type="SOURCE" refId="11"/>
<end type="TARGET" refId="12"/>
</dependency>
<association id="32">
<end type="SOURCE" refId="3" navigable="false">
<attribute id="33" name="cakeBakingService"/>
<multiplicity id="34" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="11" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<dependency id="35">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="1"/>
</dependency>
<dependency id="36">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="2"/>
</dependency>
<dependency id="37">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="4"/>
</dependency>
<dependency id="38">
<end type="SOURCE" refId="11"/>
<end type="TARGET" refId="8"/>
</dependency>
<association id="39">
<end type="SOURCE" refId="9" navigable="false">
<attribute id="40" name="cakeLayerInfos"/>
<multiplicity id="41" minimum="0" maximum="2147483647"/>
</end>
<end type="TARGET" refId="12" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<realization id="42">
<end type="SOURCE" refId="3"/>
<end type="TARGET" refId="7"/>
</realization>
<dependency id="43">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="12"/>
</dependency>
<dependency id="44">
<end type="SOURCE" refId="11"/>
<end type="TARGET" refId="9"/>
</dependency>
<association id="45">
<end type="SOURCE" refId="14" navigable="false">
<attribute id="46" name="cake"/>
<multiplicity id="47" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="13" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<dependency id="48">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="6"/>
</dependency>
<dependency id="49">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="8"/>
</dependency>
<dependency id="50">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="9"/>
</dependency>
<dependency id="51">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="13"/>
</dependency>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="true" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</classifier-display>
<association-display labels="true" multiplicity="true"/>
</class-diagram>

19
layers/index.md Normal file
View File

@ -0,0 +1,19 @@
---
layout: pattern
title: Layers
folder: layers
permalink: /patterns/layers/
categories: pattern_cat
tags: pattern_tag
---
**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

36
layers/pom.xml Normal file
View File

@ -0,0 +1,36 @@
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.5.0</version>
</parent>
<groupId>com.iluwatar.layers</groupId>
<artifactId>layers</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,108 @@
package com.iluwatar.layers;
import java.util.Arrays;
/**
*
* <p>
* Layers is an architectural style where software responsibilities are
* divided among the different layers of the application.
* </p>
*
* <p>
* This example demonstrates a traditional 3-layer architecture consisting of data access
* layer, business layer and presentation layer.
* </p>
*
* <p>
* The data access layer is formed of Spring Data repositories <code>CakeDao</code>, <code>CakeToppingDao</code> and
* <code>CakeLayerDao</code>. The repositories can be used for CRUD operations on cakes, cake toppings
* and cake layers respectively.
* </p>
*
* <p>
* The business layer is built on top of the data access layer. <code>CakeBakingService</code> 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.
* </p>
*
* <p>
* The presentation layer is built on the business layer and in this example it simply lists
* the cakes that have been baked.
* </p>
*
* <p>
* 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 (<code>CakeInfo</code>, <code>CakeToppingInfo</code>, <code>CakeLayerInfo</code>)
* to translate data between layers. In other words, <code>CakeBakingService</code> cannot
* return entities (<code>Cake</code>, <code>CakeTopping</code>, <code>CakeLayer</code>)
* directly since these reside on data access layer but instead translates these into business
* layer DTOs (<code>CakeInfo</code>, <code>CakeToppingInfo</code>, <code>CakeLayerInfo</code>)
* 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.
* </p>
*
* @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
* @param cakeBakingService
*/
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();
}
}
}

View File

@ -0,0 +1,68 @@
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<CakeLayer> 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<CakeLayer> getLayers() {
return layers;
}
public void setLayers(Set<CakeLayer> 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());
}
}

View File

@ -0,0 +1,18 @@
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);
}
}

View File

@ -0,0 +1,48 @@
package com.iluwatar.layers;
import java.util.List;
/**
*
* Service for cake baking operations
*
*/
public interface CakeBakingService {
/**
* Bakes new cake according to parameters
* @param cakeInfo
* @throws CakeBakingException
*/
void bakeNewCake(CakeInfo cakeInfo) throws CakeBakingException;
/**
* Get all cakes
* @return
*/
List<CakeInfo> getAllCakes();
/**
* Store new cake topping
* @param toppingInfo
*/
void saveNewTopping(CakeToppingInfo toppingInfo);
/**
* Get available cake toppings
* @return
*/
List<CakeToppingInfo> getAvailableToppings();
/**
* Add new cake layer
* @param layerInfo
*/
void saveNewLayer(CakeLayerInfo layerInfo);
/**
* Get available cake layers
* @return
*/
List<CakeLayerInfo> getAvailableLayers();
}

View File

@ -0,0 +1,149 @@
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<CakeToppingInfo> allToppings = getAvailableToppings();
List<CakeToppingInfo> matchingToppings = allToppings.stream()
.filter((t) -> t.name.equals(cakeInfo.cakeToppingInfo.name)).collect(Collectors.toList());
if (matchingToppings.isEmpty()) {
throw new CakeBakingException(String.format("Topping %s is not available", cakeInfo.cakeToppingInfo.name));
}
List<CakeLayer> allLayers = getAvailableLayerEntities();
Set<CakeLayer> foundLayers = new HashSet<>();
for (CakeLayerInfo info: cakeInfo.cakeLayerInfos) {
Optional<CakeLayer> 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().id.get());
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<CakeTopping> getAvailableToppingEntities() {
CakeToppingDao bean = context.getBean(CakeToppingDao.class);
List<CakeTopping> result = new ArrayList<>();
Iterator<CakeTopping> iterator = bean.findAll().iterator();
while (iterator.hasNext()) {
CakeTopping topping = iterator.next();
if (topping.getCake() == null) {
result.add(topping);
}
}
return result;
}
@Override
public List<CakeToppingInfo> getAvailableToppings() {
CakeToppingDao bean = context.getBean(CakeToppingDao.class);
List<CakeToppingInfo> result = new ArrayList<>();
Iterator<CakeTopping> 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<CakeLayer> getAvailableLayerEntities() {
CakeLayerDao bean = context.getBean(CakeLayerDao.class);
List<CakeLayer> result = new ArrayList<>();
Iterator<CakeLayer> iterator = bean.findAll().iterator();
while (iterator.hasNext()) {
CakeLayer next = iterator.next();
if (next.getCake() == null) {
result.add(next);
}
}
return result;
}
@Override
public List<CakeLayerInfo> getAvailableLayers() {
CakeLayerDao bean = context.getBean(CakeLayerDao.class);
List<CakeLayerInfo> result = new ArrayList<>();
Iterator<CakeLayer> 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<CakeInfo> getAllCakes() {
CakeDao cakeBean = context.getBean(CakeDao.class);
List<CakeInfo> result = new ArrayList<>();
Iterator<Cake> 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<CakeLayerInfo> cakeLayerInfos = new ArrayList<CakeLayerInfo>();
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;
}
}

View File

@ -0,0 +1,14 @@
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<Cake, Long> {
}

View File

@ -0,0 +1,40 @@
package com.iluwatar.layers;
import java.util.List;
import java.util.Optional;
/**
*
* DTO for cakes
*
*/
public class CakeInfo {
public final Optional<Long> id;
public final CakeToppingInfo cakeToppingInfo;
public final List<CakeLayerInfo> cakeLayerInfos;
public CakeInfo(Long id, CakeToppingInfo cakeToppingInfo, List<CakeLayerInfo> cakeLayerInfos) {
this.id = Optional.of(id);
this.cakeToppingInfo = cakeToppingInfo;
this.cakeLayerInfos = cakeLayerInfos;
}
public CakeInfo(CakeToppingInfo cakeToppingInfo, List<CakeLayerInfo> cakeLayerInfos) {
this.id = Optional.empty();
this.cakeToppingInfo = cakeToppingInfo;
this.cakeLayerInfos = cakeLayerInfos;
}
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.get(), cakeToppingInfo,
cakeLayerInfos, calculateTotalCalories());
}
}

View File

@ -0,0 +1,72 @@
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 void setName(String name) {
this.name = name;
}
public 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;
}
}

View File

@ -0,0 +1,14 @@
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<CakeLayer, Long> {
}

View File

@ -0,0 +1,32 @@
package com.iluwatar.layers;
import java.util.Optional;
/**
*
* DTO for cake layers
*
*/
public class CakeLayerInfo {
public final Optional<Long> id;
public final String name;
public final int calories;
public CakeLayerInfo(Long id, String name, int calories) {
this.id = Optional.of(id);
this.name = name;
this.calories = calories;
}
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.get(), name, calories);
}
}

View File

@ -0,0 +1,72 @@
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 void setName(String name) {
this.name = name;
}
public 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", name, calories);
}
public Cake getCake() {
return cake;
}
public void setCake(Cake cake) {
this.cake = cake;
}
}

View File

@ -0,0 +1,14 @@
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<CakeTopping, Long> {
}

View File

@ -0,0 +1,32 @@
package com.iluwatar.layers;
import java.util.Optional;
/**
*
* DTO for cake toppings
*
*/
public class CakeToppingInfo {
public final Optional<Long> id;
public final String name;
public final int calories;
public CakeToppingInfo(Long id, String name, int calories) {
this.id = Optional.of(id);
this.name = name;
this.calories = calories;
}
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.get(), name, calories);
}
}

View File

@ -0,0 +1,19 @@
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));
}
}

View File

@ -0,0 +1,12 @@
package com.iluwatar.layers;
/**
*
* View interface
*
*/
public interface View {
void render();
}

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="jpaData" />
</persistence>

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<jpa:repositories base-package="com.iluwatar" />
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:~/databases/cake" />
<property name="username" value="sa" />
<property name="password" value="sa" />
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.iluwatar" />
<property name="persistenceProvider">
<bean class="org.hibernate.ejb.HibernatePersistence" />
</property>
<property name="jpaProperties">
<map>
<entry key="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
<entry key="hibernate.hbm2ddl.auto" value="create-drop" />
<entry key="hibernate.show_sql" value="false" />
</map>
</property>
</bean>
</beans>

View File

@ -74,6 +74,7 @@
<module>business-delegate</module>
<module>half-sync-half-async</module>
<module>step-builder</module>
<module>layers</module>
</modules>
<dependencyManagement>