diff --git a/business-delegate/README.md b/business-delegate/README.md index b00c67819..886781632 100644 --- a/business-delegate/README.md +++ b/business-delegate/README.md @@ -9,21 +9,156 @@ tags: --- ## 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. +## Explanation + +Real world example + +> A mobile phone application promises to stream any movie in existence to your phone. It captures +> the user's search string and passes this on to the business delegate. The business delegate +> selects the most suitable video streaming service and plays the video from there. + +In Plain Words + +> Business delegate adds an abstraction layer between the presentation and business tiers. + +Wikipedia says + +> Business delegate is a Java EE design pattern. This pattern is directing to reduce the coupling +> in between business services and the connected presentation tier, and to hide the implementation +> details of services (including lookup and accessibility of EJB architecture). Business delegates +> acts as an adaptor to invoke business objects from the presentation tier. + +**Programmatic Example** + +First, we have an abstraction for video streaming services and a couple of implementations. + +```java +public interface VideoStreamingService { + void doProcessing(); +} + +@Slf4j +public class NetflixService implements VideoStreamingService { + @Override + public void doProcessing() { + LOGGER.info("NetflixService is now processing"); + } +} + +@Slf4j +public class YouTubeService implements VideoStreamingService { + @Override + public void doProcessing() { + LOGGER.info("YouTubeService is now processing"); + } +} +``` + +Then we have a lookup service that decides which video streaming service is used. + +```java +@Setter +public class BusinessLookup { + + private NetflixService netflixService; + private YouTubeService youTubeService; + + public VideoStreamingService getBusinessService(String movie) { + if (movie.toLowerCase(Locale.ROOT).contains("die hard")) { + return netflixService; + } else { + return youTubeService; + } + } +} +``` + +The business delegate uses a business lookup to route movie playback requests to a suitable +video streaming service. + +```java +@Setter +public class BusinessDelegate { + + private BusinessLookup lookupService; + + public void playbackMovie(String movie) { + VideoStreamingService videoStreamingService = lookupService.getBusinessService(movie); + videoStreamingService.doProcessing(); + } +} +``` + +The mobile client utilizes business delegate to call the business tier. + +```java +public class MobileClient { + + private final BusinessDelegate businessDelegate; + + public MobileClient(BusinessDelegate businessDelegate) { + this.businessDelegate = businessDelegate; + } + + public void playbackMovie(String movie) { + businessDelegate.playbackMovie(movie); + } +} +``` + +Finally, we can show the full example in action. + +```java + public static void main(String[] args) { + + // prepare the objects + var businessDelegate = new BusinessDelegate(); + var businessLookup = new BusinessLookup(); + businessLookup.setNetflixService(new NetflixService()); + businessLookup.setYouTubeService(new YouTubeService()); + businessDelegate.setLookupService(businessLookup); + + // create the client and use the business delegate + var client = new MobileClient(businessDelegate); + client.playbackMovie("Die Hard 2"); + client.playbackMovie("Maradona: The Greatest Ever"); + } +``` + +Here is the console output. + +``` +21:15:33.790 [main] INFO com.iluwatar.business.delegate.NetflixService - NetflixService is now processing +21:15:33.794 [main] INFO com.iluwatar.business.delegate.YouTubeService - YouTubeService is now processing +``` + ## Class diagram -![alt text](./etc/business-delegate.png "Business Delegate") + +![alt text](./etc/business-delegate.urm.png "Business Delegate") + +## Related patterns + +* [Service locator pattern](https://java-design-patterns.com/patterns/service-locator/) ## 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 +* 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 + +## Tutorials + +* [Business Delegate Pattern at TutorialsPoint](https://www.tutorialspoint.com/design_pattern/business_delegate_pattern.htm) ## Credits * [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=48d37c67fb3d845b802fa9b619ad8f31) +* [Core J2EE Patterns: Best Practices and Design Strategies](https://www.amazon.com/gp/product/0130648841/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0130648841&linkId=a0100de2b28c71ede8db1757fb2b5947) diff --git a/business-delegate/etc/business-delegate.png b/business-delegate/etc/business-delegate.png deleted file mode 100644 index 928cf9346..000000000 Binary files a/business-delegate/etc/business-delegate.png and /dev/null differ diff --git a/business-delegate/etc/business-delegate.ucls b/business-delegate/etc/business-delegate.ucls deleted file mode 100644 index 668a6579e..000000000 --- a/business-delegate/etc/business-delegate.ucls +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/business-delegate/etc/business-delegate.urm.png b/business-delegate/etc/business-delegate.urm.png new file mode 100644 index 000000000..4dca6c263 Binary files /dev/null and b/business-delegate/etc/business-delegate.urm.png differ diff --git a/business-delegate/etc/business-delegate.urm.puml b/business-delegate/etc/business-delegate.urm.puml index 40aa2d6f0..407e3e12d 100644 --- a/business-delegate/etc/business-delegate.urm.puml +++ b/business-delegate/etc/business-delegate.urm.puml @@ -5,53 +5,42 @@ package com.iluwatar.business.delegate { + main(args : String[]) {static} } class BusinessDelegate { - - businessService : BusinessService - lookupService : BusinessLookup - - serviceType : ServiceType + BusinessDelegate() - + doTask() - + setLookupService(businessLookup : BusinessLookup) - + setServiceType(serviceType : ServiceType) + + playbackMovie(movie : String) + + setLookupService(lookupService : BusinessLookup) } class BusinessLookup { - - ejbService : EjbService - - jmsService : JmsService + - netflixService : NetflixService + - youTubeService : YouTubeService + BusinessLookup() - + getBusinessService(serviceType : ServiceType) : BusinessService - + setEjbService(ejbService : EjbService) - + setJmsService(jmsService : JmsService) + + getBusinessService(movie : String) : VideoStreamingService + + setNetflixService(netflixService : NetflixService) + + setYouTubeService(youTubeService : YouTubeService) } - interface BusinessService { + class MobileClient { + - businessDelegate : BusinessDelegate + + MobileClient(businessDelegate : BusinessDelegate) + + playbackMovie(movie : String) + } + class NetflixService { + - LOGGER : Logger {static} + + NetflixService() + + doProcessing() + } + interface VideoStreamingService { + doProcessing() {abstract} } - class Client { - - businessDelegate : BusinessDelegate - + Client(businessDelegate : BusinessDelegate) - + doTask() - } - class EjbService { + class YouTubeService { - LOGGER : Logger {static} - + EjbService() + + YouTubeService() + doProcessing() } - class JmsService { - - LOGGER : Logger {static} - + JmsService() - + doProcessing() - } - enum ServiceType { - + EJB {static} - + JMS {static} - + valueOf(name : String) : ServiceType {static} - + values() : ServiceType[] {static} - } } -BusinessLookup --> "-ejbService" EjbService -BusinessDelegate --> "-serviceType" ServiceType -Client --> "-businessDelegate" BusinessDelegate -BusinessDelegate --> "-businessService" BusinessService +BusinessLookup --> "-netflixService" NetflixService +BusinessLookup --> "-youTubeService" YouTubeService +MobileClient --> "-businessDelegate" BusinessDelegate BusinessDelegate --> "-lookupService" BusinessLookup -BusinessLookup --> "-jmsService" JmsService -EjbService ..|> BusinessService -JmsService ..|> BusinessService +NetflixService ..|> VideoStreamingService +YouTubeService ..|> VideoStreamingService @enduml \ No newline at end of file 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 index 8bd4d12c6..e87ca9c64 100644 --- a/business-delegate/src/main/java/com/iluwatar/business/delegate/App.java +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/App.java @@ -33,9 +33,9 @@ package com.iluwatar.business.delegate; * 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. + *

In this example the client ({@link MobileClient}) utilizes a business delegate ( + * {@link BusinessDelegate}) to search for movies in video streaming services. The Business Delegate + * then selects the appropriate service and makes the service call. */ public class App { @@ -46,18 +46,16 @@ public class App { */ public static void main(String[] args) { + // prepare the objects var businessDelegate = new BusinessDelegate(); var businessLookup = new BusinessLookup(); - businessLookup.setEjbService(new EjbService()); - businessLookup.setJmsService(new JmsService()); - + businessLookup.setNetflixService(new NetflixService()); + businessLookup.setYouTubeService(new YouTubeService()); businessDelegate.setLookupService(businessLookup); - businessDelegate.setServiceType(ServiceType.EJB); - var client = new Client(businessDelegate); - client.doTask(); - - businessDelegate.setServiceType(ServiceType.JMS); - client.doTask(); + // create the client and use the business delegate + var client = new MobileClient(businessDelegate); + client.playbackMovie("Die Hard 2"); + client.playbackMovie("Maradona: The Greatest Ever"); } } 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 index d1255bf5f..6246145e7 100644 --- a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessDelegate.java +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessDelegate.java @@ -23,24 +23,18 @@ package com.iluwatar.business.delegate; +import lombok.Setter; + /** * BusinessDelegate separates the presentation and business tiers. */ +@Setter public class BusinessDelegate { private BusinessLookup lookupService; - private ServiceType serviceType; - public void setLookupService(BusinessLookup businessLookup) { - this.lookupService = businessLookup; - } - - public void setServiceType(ServiceType serviceType) { - this.serviceType = serviceType; - } - - public void doTask() { - BusinessService businessService = lookupService.getBusinessService(serviceType); - businessService.doProcessing(); + public void playbackMovie(String movie) { + VideoStreamingService videoStreamingService = lookupService.getBusinessService(movie); + videoStreamingService.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 index 07ad6342e..0369c04e8 100644 --- a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessLookup.java +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessLookup.java @@ -23,6 +23,7 @@ package com.iluwatar.business.delegate; +import java.util.Locale; import lombok.Setter; /** @@ -31,21 +32,21 @@ import lombok.Setter; @Setter public class BusinessLookup { - private EjbService ejbService; + private NetflixService netflixService; - private JmsService jmsService; + private YouTubeService youTubeService; /** - * Gets service instance based on service type. + * Gets service instance based on given movie search string. * - * @param serviceType Type of service instance to be returned. + * @param movie Search string for the movie. * @return Service instance. */ - public BusinessService getBusinessService(ServiceType serviceType) { - if (serviceType.equals(ServiceType.EJB)) { - return ejbService; + public VideoStreamingService getBusinessService(String movie) { + if (movie.toLowerCase(Locale.ROOT).contains("die hard")) { + return netflixService; } else { - return jmsService; + return youTubeService; } } } diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/Client.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/MobileClient.java similarity index 84% rename from business-delegate/src/main/java/com/iluwatar/business/delegate/Client.java rename to business-delegate/src/main/java/com/iluwatar/business/delegate/MobileClient.java index 4d5c151e3..2cfb6f344 100644 --- a/business-delegate/src/main/java/com/iluwatar/business/delegate/Client.java +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/MobileClient.java @@ -24,17 +24,17 @@ package com.iluwatar.business.delegate; /** - * Client utilizes BusinessDelegate to call the business tier. + * MobileClient utilizes BusinessDelegate to call the business tier. */ -public class Client { +public class MobileClient { private final BusinessDelegate businessDelegate; - public Client(BusinessDelegate businessDelegate) { + public MobileClient(BusinessDelegate businessDelegate) { this.businessDelegate = businessDelegate; } - public void doTask() { - businessDelegate.doTask(); + public void playbackMovie(String movie) { + businessDelegate.playbackMovie(movie); } } diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/EjbService.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/NetflixService.java similarity index 89% rename from business-delegate/src/main/java/com/iluwatar/business/delegate/EjbService.java rename to business-delegate/src/main/java/com/iluwatar/business/delegate/NetflixService.java index 6813dfec1..ae9da8747 100644 --- a/business-delegate/src/main/java/com/iluwatar/business/delegate/EjbService.java +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/NetflixService.java @@ -26,13 +26,13 @@ package com.iluwatar.business.delegate; import lombok.extern.slf4j.Slf4j; /** - * Service EJB implementation. + * NetflixService implementation. */ @Slf4j -public class EjbService implements BusinessService { +public class NetflixService implements VideoStreamingService { @Override public void doProcessing() { - LOGGER.info("EjbService is now processing"); + LOGGER.info("NetflixService is now processing"); } } diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessService.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/VideoStreamingService.java similarity index 92% rename from business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessService.java rename to business-delegate/src/main/java/com/iluwatar/business/delegate/VideoStreamingService.java index 20845841f..3c8b7e3fb 100644 --- a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessService.java +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/VideoStreamingService.java @@ -24,9 +24,9 @@ package com.iluwatar.business.delegate; /** - * Interface for service implementations. + * Interface for video streaming service implementations. */ -public interface BusinessService { +public interface VideoStreamingService { void doProcessing(); } diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/JmsService.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/YouTubeService.java similarity index 89% rename from business-delegate/src/main/java/com/iluwatar/business/delegate/JmsService.java rename to business-delegate/src/main/java/com/iluwatar/business/delegate/YouTubeService.java index 932c5038d..aa79e7309 100644 --- a/business-delegate/src/main/java/com/iluwatar/business/delegate/JmsService.java +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/YouTubeService.java @@ -26,13 +26,13 @@ package com.iluwatar.business.delegate; import lombok.extern.slf4j.Slf4j; /** - * Service JMS implementation. + * YouTubeService implementation. */ @Slf4j -public class JmsService implements BusinessService { +public class YouTubeService implements VideoStreamingService { @Override public void doProcessing() { - LOGGER.info("JmsService is now processing"); + LOGGER.info("YouTubeService is now processing"); } } 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 index b59759328..8cd5e2021 100644 --- a/business-delegate/src/test/java/com/iluwatar/business/delegate/BusinessDelegateTest.java +++ b/business-delegate/src/test/java/com/iluwatar/business/delegate/BusinessDelegateTest.java @@ -26,25 +26,20 @@ package com.iluwatar.business.delegate; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; + +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; /** - * 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. + * Tests for the {@link BusinessDelegate} */ class BusinessDelegateTest { - private EjbService ejbService; + private NetflixService netflixService; - private JmsService jmsService; + private YouTubeService youTubeService; private BusinessDelegate businessDelegate; @@ -54,19 +49,19 @@ class BusinessDelegateTest { */ @BeforeEach public void setup() { - ejbService = spy(new EjbService()); - jmsService = spy(new JmsService()); + netflixService = spy(new NetflixService()); + youTubeService = spy(new YouTubeService()); BusinessLookup businessLookup = spy(new BusinessLookup()); - businessLookup.setEjbService(ejbService); - businessLookup.setJmsService(jmsService); + businessLookup.setNetflixService(netflixService); + businessLookup.setYouTubeService(youTubeService); businessDelegate = spy(new BusinessDelegate()); businessDelegate.setLookupService(businessLookup); } /** - * In this example the client ({@link Client}) utilizes a business delegate ( + * In this example the client ({@link MobileClient}) utilizes a business delegate ( * {@link BusinessDelegate}) to execute a task. The Business Delegate then selects the appropriate * service and makes the service call. */ @@ -74,26 +69,20 @@ class BusinessDelegateTest { void testBusinessDelegate() { // setup a client object - var client = new Client(businessDelegate); - - // set the service type - businessDelegate.setServiceType(ServiceType.EJB); + var client = new MobileClient(businessDelegate); // action - client.doTask(); + client.playbackMovie("Die hard"); - // 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); + // verifying that the businessDelegate was used by client during playbackMovie() method. + verify(businessDelegate).playbackMovie(anyString()); + verify(netflixService).doProcessing(); // action - client.doTask(); + client.playbackMovie("Maradona"); // verifying that the businessDelegate was used by client during doTask() method. - verify(businessDelegate, times(2)).doTask(); - verify(jmsService).doProcessing(); + verify(businessDelegate, times(2)).playbackMovie(anyString()); + verify(youTubeService).doProcessing(); } } diff --git a/model-view-viewmodel/README.md b/model-view-viewmodel/README.md new file mode 100644 index 000000000..6df1ea5d7 --- /dev/null +++ b/model-view-viewmodel/README.md @@ -0,0 +1,145 @@ +--- +layout: pattern +title: Model-View-ViewModel +folder: model-view-viewmodel +permalink: /patterns/model-view-viewmodel/ +categories: Architectural +tags: + - Decoupling +--- + +## Also known as + +Model–View–Binder + +## Intent + +To apply "[Separation of Concerns](https://java-design-patterns.com/principles/#separation-of-concerns)" to separate the logic from the UI components and allow developers to work on UI without affecting the logic and vice versa. + +## Explanation + +Wikipedia says + +> Model–view–viewmodel (MVVM) is a software architectural pattern that facilitates the separation of the development of the graphical user interface (the view) – be it via a markup language or GUI code – from the development of the business logic or back-end logic (the model) so that the view is not dependent on any specific model platform. + +**Programmatic Example** + +Zkoss implementation: + +> ViewModel will hold the business logic and expose the data from model to View + +```java +public class BookViewModel { + @WireVariable + private List bookList; + private Book selectedBook; + private BookService bookService = new BookServiceImpl(); + + public Book getSelectedBook() { + return selectedBook; + } + + @NotifyChange("selectedBook") + public void setSelectedBook(Book selectedBook) { + this.selectedBook = selectedBook; + } + + public List getBookList() { + return bookService.load(); + } + + /** Deleting a book. + */ + @Command + @NotifyChange({"selectedBook","bookList"}) + public void deleteBook() { + if (selectedBook != null) { + getBookList().remove(selectedBook); + selectedBook = null; + } +} +``` + +> View will have no logic, only UI elements + +```xml + + + + + + + + + + + + +