An Adapter pattern acts as a connector between two incompatible interfaces that otherwise cannot be connected directly. An Adapter wraps an existing class with a new interface so that it becomes compatible with the client’s interface.
The main motive behind using this pattern is to convert an existing interface into another interface that the client expects. It’s usually implemented once the application is designed.
An example we are going to use is electric power that is used in the world in which there are two types. For the US, the electric power is 110 wats and for the rest of the world, the outlet electric power is 220 wats. We are going to have an adapter that gets the power wats based on the country.
public interface WorldElectricPower { double output = 220; double connect(); } public interface UsElectricPower { double output = 110; double connect(); } public interface PowerConnector { void plugIn(); } public class PhoneCharger implements PowerConnector { PowerAdapter adapter = null; PowerInputType powerType = null; public PhoneCharger(PowerInputType powerType){ this.powerType = powerType; adapter = new PowerAdapter(powerType); } @Override public void plugIn() { double power = adapter.connect(); System.out.println("charge connecting to "+powerType+" power at "+power+" wats"); } } public class TV implements PowerConnector { PowerAdapter adapter = null; PowerInputType powerType = null; public TV(PowerInputType powerType){ this.powerType = powerType; adapter = new PowerAdapter(powerType); } @Override public void plugIn() { double power = adapter.connect(); System.out.println("tv connecting to "+powerType+" power at "+power+" wats"); } }
public class PowerAdapter implements WorldElectricPower, UsElectricPower{ private PowerInputType powerType; public PowerAdapter(PowerInputType powerType){ this.powerType = powerType; } @Override public double connect() { return (powerType!=null && powerType.equals(PowerInputType.US)) ? UsElectricPower.output : WorldElectricPower.output; } }
public class StructuralDesignPatternDemo { public static void main(String[] args) { PhoneCharger usCharger = new PhoneCharger(PowerInputType.US); usCharger.plugIn(); TV worldTv = new TV(PowerInputType.WORLD); worldTv.plugIn(); TV usTv = new TV(PowerInputType.US); usTv.plugIn(); } }
Result
charge connecting to US power at 110.0 wats tv connecting to WORLD power at 220.0 wats tv connecting to US power at 110.0 watsAugust 4, 2019
Abstract Factory
August 4, 2019Problem: Imagine that you must make your code work with a broad set of objects that belong to a sophisticated library or framework. Ordinarily, you’d need to initialize all of those objects, keep track of dependencies, execute methods in the correct order, and so on.
As a result, the business logic of your classes would become tightly coupled to the implementation details of 3rd-party classes, making it hard to comprehend and maintain.
Facade provides a unified or a single-point interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use. This subsystem can be a library, a framework, or any other complex set of classes.
A real-world example is implementing Stripe as a Payment Gateway into your system. At first, you will need a couple of classes from Stripe to get started like Customer, Plan, Card, BankAccount, Subscription, etc. Then they have their own interfaces. As you can see, the number of interfaces you need to deal with can be a lot. You will then be concerned about decoupling Stripe from the rest of your application so that if you decide one day that Stripe is not for you that you can easily switch to Braintree or something else. Having a facade as a single point of interaction is very helpful. You can switch from StripeFacade to a BrainTreeFacade or something like that.
Applicability
1. Use the Facade pattern when you need to have a limited but straightforward interface to a complex subsystem.
2. Often, subsystems get more complex over time. Even applying design patterns typically leads to creating more classes. A subsystem may become more flexible and easier to reuse in various contexts, but the amount of configuration and boilerplate code it demands from a client grows ever larger. The Facade attempts to fix this problem by providing a shortcut to the most-used features of the subsystem which fit most client requirements.
3. Use the Facade when you want to structure a subsystem into layers.
4. Create facades to define entry points to each level of a subsystem. You can reduce coupling between multiple subsystems by requiring them to communicate only through facades.
Here is the example with Stripe.
public interface FacadeService { Account createAccount(String username, String email, String password); Account addPaymentMethod(Account account, PaymentMethod paymentMethod); Payment makePayment(PaymentMethod paymentMethod, double amount); } public class FacadeServiceImp implements FacadeService { private AccountService accountService; private PaymentMethodService paymentMethodService; private PaymentService paymentService; // there can more services here. private SubscriptionService subscriptionService; private EmailService emailService; public FacadeServiceImp() { super(); accountService = new AccountService(); paymentMethodService = new PaymentMethodService(); paymentService = new PaymentService(); } @Override public Account createAccount(String username, String email, String password) { // TODO Auto-generated method stub return accountService.create(username, email, password); } @Override public Account addPaymentMethod(Account account, PaymentMethod paymentMethod) { // TODO Auto-generated method stub return paymentMethodService.add(account, paymentMethod); } @Override public Payment makePayment(PaymentMethod paymentMethod, double amount) { // TODO Auto-generated method stub return paymentService.charge(paymentMethod, amount); } }
public class FacadeDemo { public static void main(String[] args) { FacadeService facadeService = new FacadeServiceImp(); String username = "folauk"; String email = "folaudev@gmail.com"; String password = "Test1234!"; Account account = facadeService.createAccount(username, email, password); PaymentMethod paymentMethod = new PaymentMethod(); paymentMethod.setAccount(account); paymentMethod.setName("Laulau"); paymentMethod.setLast4("4242"); account = facadeService.addPaymentMethod(account, paymentMethod); double chargeAmount = 100; Payment payment = facadeService.makePayment(paymentMethod, chargeAmount); } }August 4, 2019
Factory method creates objects of a superclass while hiding how the implementation of the creation. Under the hood, it is still created using the new keyword.
When to use:
How to implement a factory method:
Here Vehicle can be a car, van, truck, etc.
public interface Vehicle { public void run(); public void stop(); public void reverse(); }
public class VehicleFactory { // this is the factory method public static Vehicle getHehicle(VehicleType vehicleType) { Vehicle vehicle = null; switch (vehicleType) { case VAN: vehicle = new Van(); break; case SPORTSCAR: vehicle = new SportsCar(); break; default: break; } return vehicle; } }
public class Van implements Vehicle { @Override public void run() { System.out.println("Van -> run..."); } @Override public void stop() { System.out.println("Van -> stop..."); } @Override public void reverse() { System.out.println("Van -> reverse..."); } }
public class SportsCar implements Vehicle { @Override public void run() { System.out.println("SportsCar -> run..."); } @Override public void stop() { System.out.println("SportsCar -> stop..."); } @Override public void reverse() { System.out.println("SportsCar -> reverse..."); } }
public static void main(String[] args) { Van van = (Van) VehicleFactory.getHehicle(VehicleType.VAN); van.run(); van.stop(); van.reverse(); System.out.println("high speed: "+VehicleType.VAN.getHighestSpeed("van")); for (VehicleType type : VehicleType.values()) { System.out.println("type: "+type.name()); } }August 4, 2019