diff --git a/role-object/README.md b/role-object/README.md index 3f71341e1..c97177d7b 100644 --- a/role-object/README.md +++ b/role-object/README.md @@ -20,14 +20,10 @@ individual objects, different contexts are kept separate and system configuratio ## Applicability Use the Role Object pattern, if: -- you want to handle a key abstraction in different contexts and you do not want to put the resulting contextspecific interfaces into the same class interface. -Words: 4895 Page 3 of 11 -- you want to handle the available roles dynamically so that they can be attached and removed on demand, that is -at runtime, rather than fixing them statically at compile-time. -- you want to treat the extensions transparently and need to preserve the logical object identity of the resulting -object conglomerate. -- you want to keep role/client pairs independent from each other so that changes to a role do not affect clients -that are not interested in that role. +- you want to handle a key abstraction in different contexts and you do not want to put the resulting context specific interfaces into the same class interface. +- you want to handle the available roles dynamically so that they can be attached and removed on demand, that is at runtime, rather than fixing them statically at compile-time. +- you want to treat the extensions transparently and need to preserve the logical object identity of the resultingobject conglomerate. +- you want to keep role/client pairs independent from each other so that changes to a role do not affect clients that are not interested in that role. ## Credits - [Hillside - Role object pattern](https://hillside.net/plop/plop97/Proceedings/riehle.pdf) diff --git a/role-object/pom.xml b/role-object/pom.xml index 9d7e9b88b..c9feb1419 100644 --- a/role-object/pom.xml +++ b/role-object/pom.xml @@ -33,6 +33,12 @@ role-object - + + + junit + junit + test + + diff --git a/role-object/src/main/java/com/iluwatar/roleobject/ApplicationRoleObject.java b/role-object/src/main/java/com/iluwatar/roleobject/ApplicationRoleObject.java index 3dc83e470..1006fe084 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/ApplicationRoleObject.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/ApplicationRoleObject.java @@ -22,6 +22,11 @@ */ package com.iluwatar.roleobject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.iluwatar.roleobject.Role.*; + /** * The Role Object pattern suggests to model context-specific views * of an object as separate role objects which are @@ -34,12 +39,12 @@ package com.iluwatar.roleobject; * investor, respectively. Both roles could as well be played by a single Customer object. * The common superclass for customer-specific roles is provided by CustomerRole, * which also supports the Customer interface. + *

* The CustomerRole class is abstract and not meant to be instantiated. * Concrete subclasses of CustomerRole, for example Borrower or Investor, * define and implement the interface for specific roles. It is only * these subclasses which are instantiated at runtime. - * The Borrower class defines the context-specific view of - * Customer objects as needed by the loan department. + * The Borrower class defines the context-specific view of Customer objects as needed by the loan department. * It defines additional operations to manage the customer’s * credits and securities. Similarly, the Investor class adds operations specific * to the investment department’s view of customers. @@ -48,13 +53,40 @@ package com.iluwatar.roleobject; * Customer instance through its Customer interface. The loan application may want to check whether the Customer * object plays the role of Borrower. * To this end it calls hasRole() with a suitable role specification. For the purpose of - * our example, let’s assume we can name roles with a simple string. - * If the Customer object can play the role named - * “Borrower,” the loan application will ask it to return a reference to the corresponding object. + * our example, let’s assume we can name roles with enum. + * If the Customer object can play the role named “Borrower,” the loan application will ask it to return a reference to the corresponding object. * The loan application may now use this reference to call Borrower-specific operations. - * */ public class ApplicationRoleObject { + + private static final Logger logger = LoggerFactory.getLogger(Role.class); + public static void main(String[] args) { + Customer customer = Customer.newCustomer(Borrower, Investor); + + logger.info(" the new customer created : {}", customer); + + boolean hasBorrowerRole = customer.hasRole(Borrower); + logger.info(" customer has a borrowed role - {}", hasBorrowerRole); + boolean hasInvestorRole = customer.hasRole(Investor); + logger.info(" customer has an investor role - {}", hasInvestorRole); + + customer.getRole(Investor, InvestorRole.class) + .ifPresent(inv -> { + inv.setAmountToInvest(1000); + inv.setName("Billy"); + }); + customer.getRole(Borrower, BorrowerRole.class) + .ifPresent(inv -> inv.setName("Johny")); + + customer.getRole(Investor, InvestorRole.class) + .map(InvestorRole::invest) + .ifPresent(logger::info); + + customer.getRole(Borrower, BorrowerRole.class) + .map(BorrowerRole::borrow) + .ifPresent(logger::info); } + + } diff --git a/role-object/src/main/java/com/iluwatar/roleobject/BorrowerRole.java b/role-object/src/main/java/com/iluwatar/roleobject/BorrowerRole.java index c900e3eed..8bc423e4b 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/BorrowerRole.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/BorrowerRole.java @@ -1,4 +1,19 @@ package com.iluwatar.roleobject; public class BorrowerRole extends CustomerRole{ + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String borrow(){ + return String.join(" ", + "A borrower",name,"wants to get some money."); + } + } diff --git a/role-object/src/main/java/com/iluwatar/roleobject/Customer.java b/role-object/src/main/java/com/iluwatar/roleobject/Customer.java index c9cdab4e1..c8bf5a857 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/Customer.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/Customer.java @@ -2,11 +2,56 @@ package com.iluwatar.roleobject; import java.util.Optional; +/** + * The main abstraction to work with Customer. + */ public abstract class Customer { + /** + * Add specific role @see {@link Role} + * + * @param role to add + * @return true if the operation has been successful otherwise false + */ public abstract boolean addRole(Role role); + + /** + * Check specific role @see {@link Role} + * + * @param role to check + * @return true if the role exists otherwise false + */ + public abstract boolean hasRole(Role role); + + /** + * Remove specific role @see {@link Role} + * + * @param role to remove + * @return true if the operation has been successful otherwise false + */ public abstract boolean remRole(Role role); - public abstract Optional getRole(Role role,Class expectedRole); + + /** + * Get specific instance associated with this role @see {@link Role} + * + * @param role to get + * @param expectedRole instance class expected to get + * @return optional with value if the instance exists and corresponds expected class + */ + public abstract Optional getRole(Role role, Class expectedRole); + + + public static Customer newCustomer() { + return new CustomerCore(); + } + + public static Customer newCustomer(Role... role) { + Customer customer = newCustomer(); + for (Role r : role) { + customer.addRole(r); + } + return customer; + } } diff --git a/role-object/src/main/java/com/iluwatar/roleobject/CustomerCore.java b/role-object/src/main/java/com/iluwatar/roleobject/CustomerCore.java index 496022c06..3c2c7d406 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/CustomerCore.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/CustomerCore.java @@ -1,19 +1,30 @@ package com.iluwatar.roleobject; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; +import java.util.*; +/** + * Core class to store different customer roles + * + * @see CustomerRole + * Note: not thread safe + */ public class CustomerCore extends Customer { private Map roles; + public CustomerCore() { + roles = new HashMap<>(); + } @Override public boolean addRole(Role role) { - return role.instance() - .map(rI -> roles.put(role, rI)) - .isPresent(); + return role + .instance() + .map(inst -> { + roles.put(role, inst); + return true; + }) + .orElse(false); } @Override @@ -28,8 +39,15 @@ public class CustomerCore extends Customer { @Override public Optional getRole(Role role, Class expectedRole) { - return Optional.ofNullable(roles.get(role)) + return Optional + .ofNullable(roles.get(role)) .filter(expectedRole::isInstance) .map(expectedRole::cast); } + + @Override + public String toString() { + String roles = Arrays.toString(this.roles.keySet().toArray()); + return "Customer{roles=" + roles + "}"; + } } diff --git a/role-object/src/main/java/com/iluwatar/roleobject/CustomerRole.java b/role-object/src/main/java/com/iluwatar/roleobject/CustomerRole.java index af2801f3d..e9806246b 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/CustomerRole.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/CustomerRole.java @@ -1,25 +1,6 @@ package com.iluwatar.roleobject; -import java.util.Optional; - -public class CustomerRole extends Customer{ - @Override - public boolean addRole(Role role) { - return false; - } - - @Override - public boolean hasRole(Role role) { - return false; - } - - @Override - public boolean remRole(Role role) { - return false; - } - - @Override - public Optional getRole(Role role, Class expectedRole) { - return Optional.empty(); - } -} +/** + * Key abstraction for segregated roles + */ +public abstract class CustomerRole extends CustomerCore{} diff --git a/role-object/src/main/java/com/iluwatar/roleobject/InvestorRole.java b/role-object/src/main/java/com/iluwatar/roleobject/InvestorRole.java index a8a85d9da..4267d4d95 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/InvestorRole.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/InvestorRole.java @@ -1,4 +1,27 @@ package com.iluwatar.roleobject; -public class InvestorRole extends CustomerRole{ +public class InvestorRole extends CustomerRole { + private String name; + private long amountToInvest; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public long getAmountToInvest() { + return amountToInvest; + } + + public void setAmountToInvest(long amountToInvest) { + this.amountToInvest = amountToInvest; + } + + public String invest() { + return String.join(" ", + "Investor", name, "has invested", String.valueOf(amountToInvest), "dollars"); + } } diff --git a/role-object/src/main/java/com/iluwatar/roleobject/Role.java b/role-object/src/main/java/com/iluwatar/roleobject/Role.java index a59d6377b..c1934f7c5 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/Role.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/Role.java @@ -2,27 +2,30 @@ package com.iluwatar.roleobject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sun.rmi.runtime.Log; import java.util.Optional; +/** + * Possible roles + */ public enum Role { Borrower(BorrowerRole.class), Investor(InvestorRole.class); - private Class typeCst; + private Class typeCst; - Role(Class typeCst) { + Role(Class typeCst) { this.typeCst = typeCst; } + private static final Logger logger = LoggerFactory.getLogger(Role.class); @SuppressWarnings("unchecked") - Optional instance(){ - Class typeCst = this.typeCst; + public Optional instance() { + Class typeCst = this.typeCst; try { return (Optional) Optional.of(typeCst.newInstance()); } catch (InstantiationException | IllegalAccessException e) { - logger.error("error creating an object",e); + logger.error("error creating an object", e); } return Optional.empty(); } diff --git a/role-object/src/test/java/com/iluwatar/roleobject/ApplicationRoleObjectTest.java b/role-object/src/test/java/com/iluwatar/roleobject/ApplicationRoleObjectTest.java new file mode 100644 index 000000000..f2c822912 --- /dev/null +++ b/role-object/src/test/java/com/iluwatar/roleobject/ApplicationRoleObjectTest.java @@ -0,0 +1,13 @@ +package com.iluwatar.roleobject; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class ApplicationRoleObjectTest { + + @Test + public void mainTest() { + ApplicationRoleObject.main(new String[]{}); + } +} \ No newline at end of file diff --git a/role-object/src/test/java/com/iluwatar/roleobject/BorrowerRoleTest.java b/role-object/src/test/java/com/iluwatar/roleobject/BorrowerRoleTest.java new file mode 100644 index 000000000..6ba26009f --- /dev/null +++ b/role-object/src/test/java/com/iluwatar/roleobject/BorrowerRoleTest.java @@ -0,0 +1,18 @@ +package com.iluwatar.roleobject; + +import org.junit.Assert; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class BorrowerRoleTest { + + @Test + public void borrowTest() { + BorrowerRole borrowerRole = new BorrowerRole(); + borrowerRole.setName("test"); + String res = "A borrower test wants to get some money."; + + Assert.assertEquals(borrowerRole.borrow(),res); + } +} \ No newline at end of file diff --git a/role-object/src/test/java/com/iluwatar/roleobject/CustomerCoreTest.java b/role-object/src/test/java/com/iluwatar/roleobject/CustomerCoreTest.java new file mode 100644 index 000000000..64712e11d --- /dev/null +++ b/role-object/src/test/java/com/iluwatar/roleobject/CustomerCoreTest.java @@ -0,0 +1,81 @@ +package com.iluwatar.roleobject; + +import org.junit.Test; + +import java.util.Optional; + +import static org.junit.Assert.*; + +public class CustomerCoreTest { + + @Test + public void addRole() { + CustomerCore core = new CustomerCore(); + boolean add = core.addRole(Role.Borrower); + assertTrue(add); + + } + + @Test + public void hasRole() { + CustomerCore core = new CustomerCore(); + core.addRole(Role.Borrower); + + boolean has = core.hasRole(Role.Borrower); + assertTrue(has); + + boolean notHas = core.hasRole(Role.Investor); + assertFalse(notHas); + } + + @Test + public void remRole() { + CustomerCore core = new CustomerCore(); + core.addRole(Role.Borrower); + + Optional bRole = core.getRole(Role.Borrower, BorrowerRole.class); + assertTrue(bRole.isPresent()); + + boolean res = core.remRole(Role.Borrower); + assertTrue(res); + + Optional empt = core.getRole(Role.Borrower, BorrowerRole.class); + assertFalse(empt.isPresent()); + + } + + @Test + public void getRole() { + CustomerCore core = new CustomerCore(); + core.addRole(Role.Borrower); + + Optional bRole = core.getRole(Role.Borrower, BorrowerRole.class); + assertTrue(bRole.isPresent()); + + Optional nonRole = core.getRole(Role.Borrower, InvestorRole.class); + assertFalse(nonRole.isPresent()); + + Optional invRole = core.getRole(Role.Investor, InvestorRole.class); + assertFalse(invRole.isPresent()); + + + } + + + @Test + public void toStringTest() { + CustomerCore core = new CustomerCore(); + core.addRole(Role.Borrower); + assertEquals(core.toString(), "Customer{roles=[Borrower]}"); + + core = new CustomerCore(); + core.addRole(Role.Investor); + assertEquals(core.toString(), "Customer{roles=[Investor]}"); + + core = new CustomerCore(); + assertEquals(core.toString(), "Customer{roles=[]}"); + + + } + +} \ No newline at end of file diff --git a/role-object/src/test/java/com/iluwatar/roleobject/InvestorRoleTest.java b/role-object/src/test/java/com/iluwatar/roleobject/InvestorRoleTest.java new file mode 100644 index 000000000..9274eae7c --- /dev/null +++ b/role-object/src/test/java/com/iluwatar/roleobject/InvestorRoleTest.java @@ -0,0 +1,18 @@ +package com.iluwatar.roleobject; + +import org.junit.Assert; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class InvestorRoleTest { + + @Test + public void investTest() { + InvestorRole investorRole = new InvestorRole(); + investorRole.setName("test"); + investorRole.setAmountToInvest(10); + String res = "Investor test has invested 10 dollars"; + Assert.assertEquals(investorRole.invest(), res); + } +} \ No newline at end of file diff --git a/role-object/src/test/java/com/iluwatar/roleobject/RoleTest.java b/role-object/src/test/java/com/iluwatar/roleobject/RoleTest.java new file mode 100644 index 000000000..dda326228 --- /dev/null +++ b/role-object/src/test/java/com/iluwatar/roleobject/RoleTest.java @@ -0,0 +1,18 @@ +package com.iluwatar.roleobject; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.Optional; + +import static org.junit.Assert.*; + +public class RoleTest { + + @Test + public void instanceTest() { + Optional instance = Role.Borrower.instance(); + Assert.assertTrue(instance.isPresent()); + Assert.assertEquals(instance.get().getClass(),BorrowerRole.class); + } +} \ No newline at end of file