I have following classes:
class ServiceA{
User getUser(){} // API call to userServiceA.com
Profile getProfile(){} // API call to profileServiceA.com
}
class ServiceB{
User getUser(){} // API call to userServiceB.com
Profile getProfile(){} // API call to profileServiceB.com
}
class GroupService(){
ServiceA serviceA;
ServiceB serviceB;
constructor(){
this.serviceA = new ServiceA();
this.serviceB = new ServiceB();
}
getUser(String type){
if(type.equals("A")){
serviceA.getUser();
}else if(type.equals("B")){
serviceB.getUser();
}
}
}
class Controller(){
get(RC routingContext){
String type = routingContext.getParam("type");
GroupService groupService = new GroupService();
groupService.getUser(type);
}
}
In this project ServiceC,D,E... will be kept adding, and that will turn the GroupService class into a chaos.
For this particular scenario, which is the correct design pattern I can apply? Factory pattern is one solution, but then if else or Map will still exist.
Update: Using Strategy Pattern
public interface ServiceAZ {
User getUser();
Profile getProfile();
}
class ServiceA implements ServiceAZ{
#Override
public User getUser() {
return // API call to userServiceA.com
}
#Override
public Profile getProfile() {
return // API call to profileServiceA.com
}
}
class ServiceB implements ServiceAZ{
#Override
public User getUser() {
return // API call to userServiceB.com
}
#Override
public Profile getProfile() {
return // API call to profileServiceB.com
}
}
class GroupService(){
private ServiceAZ service;
GroupService(ServiceAZ s){
this.service = s;
}
User getUser(){
service.getUser();
}
}
class Controller(){
get(routingContext){
String type = routingContext.getParam("type");
ServiceAZ service;
if(type.equals("A")){
service = new ServiceA();
}
if(type.equals("B")){
service = new ServiceB();
}
GroupService groupService = new GroupService(service);
groupService.getUser(type);
}
}
This looks much better but now in the controller, the if else is a problem
Related
I'm working on updating some legacy code for a jboss application and I've run into a bit of a jam. I have two ejb beans, PersonBean and ClientBean. ClientBean is responsible for handling client specific services. ClientBean gets injected with an instance of PersonBean which it uses as a delegate to pass along requests to server side services. The problem I'm running into is that both beans also implement a LoginService interface which also needs to get injected. What I want is for an instance of PersonBean to get injected into ClientBean as the loginService but I'm ending up with ClientBean being injected into itself.
What do I need to do to correctly define this ejb mapping?
ClientServiceProviderBean:
#Stateless(name = "ClientServiceProvider")
#Local({ ClientServiceProvider.class, LoginService.class })
public class ClientServiceProviderBean implements ClientServiceProvider, LoginService
{
#EJB(name = "personService")
protected PersonService personService;
#EJB(name = "loginService")
protected LoginService loginService;
#Override
public LoginDTO getLoggedInUser()
{
LoginDTO loginDTO = loginService.getLoggedInUser();
return loginDTO;
}
#Override
public Long activateSession(String applicationName, String ipAddress)
{
return personService.activateSession(applicationName, ipAddress);
}
}
PersonServiceBean:
#Stateless(name = "PersonService")
#Local({ PersonService.class })
#Remote({ RemotePersonService.class })
public class PersonServiceBean implements PersonService, RemotePersonService, LoginService
{
#Override
#RolesAllowed({ "authenticated" })
public Long activateSession(String applicationName, String ipAddress)
{
Person p = getCallerAsPerson(entityManager, context.getCallerPrincipal());
SessionActivity sessionActivity = new SessionActivity(p.getId(), applicationName, true, ipAddress);
sessionActivity = save(entityManager, sessionActivity);
return sessionActivity.getId();
}
#Override
#PermitAll
public LoginDTO getLoggedInUser()
{
Principal p = context.getCallerPrincipal();
if (p != null && !"unauthenticated".equals(p.getName()))
{
try
{
Person person = getCallerAsPerson(entityManager, p);
if (person != null)
{
return createLoginDTO(person);
}
}
catch (javax.persistence.NoResultException e)
{
}
}
return null;
}
}
If you want to inject PersonServiceBean as a LoginService in ClientServiceProviderBean you should add the LoginService.class in the #Local anotation of PersonServiceBean and remove it from ClientServiceProviderBean.
I want a factory class that return a service that I can use to do some validations. I implemented this class
public class EventUpdateValidatorFactory {
public EventUpdateValidatorStrategy getValidator(EEventStatus eventStatus) {
if (SECOND_APPROVAL.equals(eventStatus)) {
return new EventSecondApprovalValidator();
} else if (APPROVED.equals(eventStatus)) {
return new EventApprovedValidator();
} else if (ACCOUNTING_HQ.equals(eventStatus)) {
return new EventAccountingHqValidator();
}
throw new IllegalArgumentException("Unknown status");
}
}
The interface EventUpdateValidatorStrategy is this
public interface EventUpdateValidatorStrategy {
default <T extends EventUpdateValidatorStrategy> void validate(User user, EventMasterData masterData, Event event, List<EventExternalSystemExpenseSave> expenses,
List<EventExternalSystemSpeakerSave> speakers, long eventId) {
this.validateMasterData(masterData, event);
this.validateSpeakers(speakers, eventId);
this.validateExpenses(expenses, eventId);
this.doUpdate(user, masterData, expenses, speakers, eventId);
}
void validateMasterData(EventMasterData masterData, Event event);
void validateExpenses(List<EventExternalSystemExpenseSave> expenses, long eventId);
void validateSpeakers(List<EventExternalSystemSpeakerSave> speakers, long eventId);
void doUpdate(User user, EventMasterData masterData, List<EventExternalSystemExpenseSave> expenses, List<EventExternalSystemSpeakerSave> speakers, long eventId);
}
The EventSecondApprovalValidator is this
#Service
#Transactional
public class EventSecondApprovalValidator implements EventUpdateValidatorStrategy {
#Autowired
private EventService eventService;
#Autowired
private ContextDateService contextDateService;
#Autowired
private EventExpenseService eventExpenseService;
#Autowired
private EventExternalSystemDAO eventExternalSystemDAO;
#Override
public void validateMasterData(LocalEventMasterData masterData, Event event) {
// some logic
}
#Override
public void validateExpenses(List<EventExternalSystemExpenseSave> expenses, long eventId) {
// some logic
}
#Override
public void validateSpeakers(List<EventExternalSystemSpeakerSave> speakers, long eventId) {
// some logic
}
#Override
public void doUpdate(User user, EventMasterData masterData, List<EventExternalSystemExpenseSave> expenses, List<EventExternalSystemSpeakerSave> speakers, long eventId) {
ofNullable(expenses).ifPresent(expensesToSave -> expensesToSave.forEach(expense -> this.eventExternalSystemDAO.updateExpense(user, expense)));
this.eventExternalSystemDAO.updateEvent(user, masterData, eventId);
}
}
The other EventApprovedValidator and EventAccountingHqValidator implementations are similar.
From main code I do this call
final EventUpdateValidatorStrategy validator = EventUpdateValidatorFactory.getValidator(event.getStatus());
validator.validate(user, eventSave.getMasterData(), event, eventSave.getExpenses(), eventSave.getSpeakers(), eventID);
and the result is that when I enter inside a EventSecondApprovalValidator all the autowired services are null and, obviously, I receive a NPE the first time that I use one of that service.
How I correctly use the factory to return the service that I need based on EEventStatus?
In EventUpdateValidatorFactory.getValidator(EEventStatus) method, you need to return the EventSecondApprovalValidator bean from context, instead of creating a new instance using new keyword.
The class EventSecondApprovalValidator is #Service annotated (and assuming there is only one of this type), an instance of this type will be added to ApplicationContext by Spring with all dependencies injected. So, just fetch it from context and use it.
One quick way to do this is as follows:
public EventUpdateValidatorStrategy getValidator(ApplicationContext context,
EEventStatus eventStatus) {
if (SECOND_APPROVAL.equals(eventStatus)) {
return context.getBean(EventSecondApprovalValidator.class);
} else if (APPROVED.equals(eventStatus)) {
return context.getBean(EventApprovedValidator.class);
} else if (ACCOUNTING_HQ.equals(eventStatus)) {
return context.getBean(EventAccountingHqValidator.class);
}
throw new IllegalArgumentException("Unknown status");
}
You can also #Autowire all validators in EventUpdateValidatorFactory and return the #Autowired instances. This will keep the getValidator method's signature same, but you'll have to make EventUpdateValidatorFactory a #Component-esque class.
#Component
public class EventUpdateValidatorFactory {
#Autowired
EventSecondApprovalValidator a;
#Autowired
EventApprovedValidator b;
#Autowired
EventAccountingHqValidator c;
public EventUpdateValidatorStrategy getValidator(EEventStatus eventStatus) {
if (SECOND_APPROVAL.equals(eventStatus)) {
return a;
} else if (APPROVED.equals(eventStatus)) {
return b;
} else if (ACCOUNTING_HQ.equals(eventStatus)) {
return c;
}
throw new IllegalArgumentException("Unknown status");
}
Creating an object manually you are not letting Spring perform autowiring. Consider managing your services by Spring as well.
#Component
public class MyServiceAdapter implements MyService {
#Autowired
private MyServiceOne myServiceOne;
#Autowired
private MyServiceTwo myServiceTwo;
#Autowired
private MyServiceThree myServiceThree;
#Autowired
private MyServiceDefault myServiceDefault;
public boolean checkStatus(String service) {
service = service.toLowerCase();
if (service.equals("one")) {
return myServiceOne.checkStatus();
} else if (service.equals("two")) {
return myServiceTwo.checkStatus();
} else if (service.equals("three")) {
return myServiceThree.checkStatus();
} else {
return myServiceDefault.checkStatus();
}
}
}
I know there are similar questions already, but looking at them I still have some doubts about how I should design my code. I have a service that allows for User registration / login /update / delete. The thing is that the User is an abstract type, which contains the data typeOfUser based on which the actual registration / update / delete methods should be called, and right now I do that in a switch-case block. I'd like to replace that with some better design.
UserController.java
public class UserController {
public UserDto register(UserDto user) {
switch(user.getTypeOfUser()) {
case DRIVER: return driverService.register(user);
case CUSTOMER: return customerService.register(user);
// ...
}
}
public UserDto update(UserDto user) {
switch(user.getTypeOfUser) {
case DRIVER: return driverService.update((DriverDto) user);
case CUSTOMER: return customerService.update((CustomerDto) user);
// ...
}
}
public UserDto login(long userId) {
loginService.login(userId);
UserBO user = userService.readById(userId);
switch(user.getTypeOfUser) {
case DRIVER: return DriverDto.fromBO((DriverBO) user);
case CUSTOMER: return CustomerDto.fromBO((CustomerBO) user);
// ...
}
}
// ...
}
I understand that something like Visitor pattern could be used, but would I really need to add the methods of registration / login /update / delete in the Enum itself? I don't really have a clear idea on how to do that, any help is appreciated.
I'd like to replace that with some better design.
The first step towards replacing the switch statement and take advantage of Polymorphism instead is to ensure that there is a single contract (read method signature) for each of the operations regardless of the user type. The following steps will explain how to achieve this :
Step 1 : Define a common interface for performing all operations
interface UserService {
public UserDto register(UserDto user);
public UserDto update(UserDto user);
public UserDto login(UserDto user)
}
Step 2 : Make UserController take a UserService as a dependency
public class UserController {
private UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
public UserDto register(UserDto user) {
userService.register(user);
}
public UserDto update(UserDto user) {
userService.update(user);
}
public UserDto login(long userId) {
userService.login(user);
}
}
Step 3 : Create subclasses to handle different types of users that take CustomerDto and CustomerBO as a dependency
class CustomerService implements UserService {
private CustomerDto userDto;
private CustomerBO userBO;
public CustomerService(UserDto userDto,UserBO userBo) {
this.userDto = (CustomerDto)userDto;
this.userBO= (CustomerBO)userBo;
}
//implement register,login and update methods to operate on userDto and userBo
}
Implement the DriverService class in a similar fashion with a dependency on DriverBo and DriverDto objects respectively.
Step 4 : Implement a runtime factory that decides which service to pass to UserController :
public UserControllerFactory {
public static void createUserController(UserDto user) {
if(user.getTypeOfUser().equals(CUSTOMER)) {
return new UserController(new CustomerService(user));
} else if(user.getTypeOfUser().equals(DRIVER)) {
return new UserController(new DriverService(user));
}
}
}
Step 5 Call the factory to create a user controller
UserDto user = someMethodThatCreatesUserDto(();
UserController controller = UserControllerFactory.createUserController(user);
controller.register();
controller.update();
controller.login();
The advantage of the above approach is that the switch/if-else statements are moved all they way back to a single class i.e the factory.
You'd want something like that:
public abstract class User {
abstract void register();
abstract void update();
abstract void login();
// maybe some more common non-abstract methods
}
Any type of User will have a class that extends this abstract class and therefore must implement all its abstract methods, like this:
public class Driver extends User {
public void register() {
// do whatever a driver does when register...
}
public void update() {
// do whatever a driver does when update...
}
public void login() {
// do whatever a driver does when login...
}
}
public class Customer extends User {
public void register() {
// do whatever a customer does when register...
}
public void update() {
// do whatever a customer does when update...
}
public void login() {
// do whatever a customer does when login...
}
}
This way, you're avoiding any switch case code. For instance, you can have an array of Users, each one them will be instantiated using new Driver() or new Customer(). Then, for example, if you're iterating over this array and executing all the Users login() method, each user's login() will be called according to its specific type ==> no switch-case needed, no casting needed!
Very simple example (only for different login logic for DriverDto and CustomerDto) - I've resigned from field typeOfUser (because it is not necessary in my solution) - I'm not sure that this is possible in your solution:
public abstract class UserDto {
// put some generic data & methods here
}
public class CustomerDto extends UserDto {
private String customerName;
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
}
public class DriverDto extends UserDto {
private String driverName;
public String getDriverName() {
return driverName;
}
public void setDriverName(String driverName) {
this.driverName = driverName;
}
}
public class ThisIsServiceOrDelegateToOtherServices {
public void login(CustomerDto customer) {
String name = customer.getCustomerName();
System.out.println(name);
// work on name here
}
public void login(DriverDto customer) {
String name = customer.getDriverName();
System.out.println(name);
// work on name here
}
}
Usage:
public static void main(String... args) {
//demo data
CustomerDto customer = new CustomerDto();
customer.setCustomerName("customerName");
DriverDto driver = new DriverDto();
driver.setDriverName("driverName");
// usage
ThisIsServiceOrDelegateToOtherServices service = new ThisIsServiceOrDelegateToOtherServices();
service.login(customer);
service.login(driver);
}
If you really need that TypeOfUser-enum in your UserDTO, then you could extend your enum with a serivce. So you create a TypeOfUserService interface. CustomerSerivce and DriverService will inherit from that service:
public interface TypeOfUserService {
public void register(UserDTO user);
// ...
}
public class CustomerService implements TypeOfUserService {
#Override
public void register(UserDTO user) {
// ...
}
}
public class DriverService implements TypeOfUserService {
#Override
public void register(UserDTO user) {
// ...
}
}
Then you create your register, update, etc. methods in your TypeOfUser enum:
public enum TypeOfUser {
DRIVER(new DriverService()),
CUSTOMER(new CustomerService());
private TypeOfUserService typeOfUserService;
TypeOfUser(TypeOfUserService typeOfUserService) {
this.typeOfUserService = typeOfUserService;
}
public static void register(String typeOfUser, UserDTO user) {
TypeOfUser.valueOf(typeOfUser).typeOfUserService.register(user);
}
// ...
}
You could then call the register method via:
class UserController() {
public UserDto register(UserDto user) {
TypeOfUser.register(user.getTypeOfUser, user);
}
}
I have an interface and two implementations of that interface.
Now on the interface I am adding '#Component' annotation. One of the implementation has a '#primary' annotation which is only getting called up.
I want to call both the implementations when I call the interface's method from the autowired interface bean.
#Component
public interface CustomerPersister {
AbuserDetails setAbuserDetails(AbuserDetails customer);
}
#Primary
#Component
public class CustomerRedisPersisterImpl implements CustomerPersister{
#Autowired
private CustomerManager customerManager;
#Override
public AbuserDetails setAbuserDetails(AbuserDetails customer) {
if(customerManager.setAbuserDetails
(customer,ATSNamespaces.ABUSERDETAILS)){
return customer;
}else{
return new AbuserDetails();
}
}
#Component
public class MongoDbRepositoryImpl implements CustomerPersister{
#Autowired
MongoTemplate mongoTemplate;
#Override
public AbuserDetails setAbuserDetails(AbuserDetails customer) {
Query query = new Query(Criteria.where("login").is(customer.getLogin()));
System.out.println("query is:"+query);
Update update = new Update();
update.set("isReturnAbuser", customer.getIsReturnAbuser());
update.set("reasonReturnAbuser", customer.getReasonReturnAbuser());
update.set("isCODThrottled", customer.getIsCODThrottled());
update.set("reasonRTOAbuser", customer.getReasonRTOAbuser());
update.set("isFakeEmail", customer.getIsFakeEmail());
update.set("reasonFakeEmail", customer.getReasonFakeEmail());
update.set("amount",customer.getAmount());
WriteResult result = mongoTemplate.upsert(query, update, AbuserDetails.class);
System.out.println("This is the class that got returned:"+result.getClass());
System.out.println("New design result:"+result);
if(result!=null){
if(result.getN() != 0)
return customer;
else
return null;
}else
return null;
}
someOtherClass
#Autowired
private CustomerPersister customerPersister;
#Override
#RequestMapping(value = "/abuser/details/set", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public AbuserDetails setAbuserDetails(#RequestBody AbuserDetails customer){
return customerPersister.setAbuserDetails(customer);
}
You can tell Spring to autowire all implementations of an interface as a List and then call the method an all implementations.
class SomeClass {
List<CustomerPersister> customerPersisters;
#Autowired
SomeClass(List<CustomerPersister> customerPersisters) {
this.customerPersisters = customerPersisters;
}
public void setAbuserDetails(#RequestBody AbuserDetails customer) {
for (CustomerPersister customerPersister: customerPersisters) {
customerPersister.setAbuserDetails(customer);
}
}
}
Of course this will not allow you to return the result of customerPersister.setAbuserDetails(), because you can't return a single value from multiple persister calls. You either have to write some code in SomeClass that will determine which object should be returned or you could return a list of the results from all persisters. Or you have to redesign your interface to match the requirements.
Let's say we have interface:
public interface IAuthentication { }
and two implementations:
public class LdapAuthentication implements IAuthentication {}
public class DbAuthentication implements IAuthentication {}
And finally we have a bean that is responsible for processing authentication. This bean should use one of the implementations shown above (based on configuration specified in for example db).
#Service
public class AuthenticationService {
public boolean authenticate(...) {
boolean useDb = ...; //got from db
//my problem here
//how to get right implementation: either LdapAuthentication or DbAuthentication?
IAuthentication auth = ...;
return auth.authenticate(...);
}
}
Question:
How to get the right implementation?
If parameter value does not change:
#Service
public class AuthenticationService {
private IAuthentication auth;
#PostConstruct
protected void init() {
boolean useDb = ...; //got from db
this.auth = ...; //choose correct one
}
public boolean authenticate(...) {
return auth.authenticate(...);
}
}
If parameter is dynamic
#Service
public class AuthenticationService {
#Autowired
private ApplicationContext сontext;
public boolean authenticate(...) {
boolean useDb = ...; //got from db
IAuthentication auth = context.getBean(useDb ? DbAuthentication.class : LdapAuthentication.class);
return auth.authenticate(...);
}
}