Adapter -> Structural Arrogant Design Pattern

Arrogant Design Patterns ( by Hafiz Waleed Hussain )

Hi, I hope everyone is doing good.

Today, we are going to discuss how to be a magician in coding :). Means we will address one of the Structural Design Pattern. In my opinion, if someone is too good with Structural Design Patterns, he/she can impress you easily by making some small changes at the abstract level. Magic will be your whole application will start working with new requirements, but without making any changes in your entire application.

In case you missed the Introductory Post of Arrogant Design Patterns.
More Posts on Design Patterns https://www.uwanttolearn.com/design-patterns/

For the official or popular definition of Structural Design Pattern, you can consult with Google. I will share my thoughts with you, and maybe you disagree with that, which is okay.

For me, Structural Patterns are something like Data Structure. All languages have Primitive Data Types like int, float, boolean. Now, we can design our Data Structure by using these Primitive types, as shown below in a Pseudo Code.

struct MyDataStructure{
public int age;
public float height;
public boolean isAdult;
}

What did we do in the above code? I took Primitive Data Types and combine them in a new class or structure by doing Composition ( Will discuss composition after this ). So I created a new Structure by using current Primitive Data Types or Structures, which will be helpful for my System.

Now, this is the same analogy that will work in Structural Design Patterns. Only the difference is, in Structural Design Patterns, we will use interfaces, inheritance, and composition to create new structures (which we will see in all posts of Structural Design Pattern). And in the case of Data Structure example, we used Primitive Data Types to create new Data Structures. I hope things are a little more clear.

Revision:

Note: Revision, I copied from my existing post on Design Patterns. If you already read this, please skip and scroll to Adapter Design Pattern heading. But I will recommend you to revise this if possible that will take only 2-3 extra minutes.

We need to do some concepts revise, which are essential from the perspective of Design Patterns.

I am sure everyone who is reading this blog is aware of how to use

  • if, else if, switch, when
  • for loop, while loop, do-while loop
  • Functions,
  • Classes, Objects
  • Inheritance, Encapsulation, Polymorphism
  • Composition
  • Dependency Injection
  • Single Responsibility Principle
  • Open-Closed Principle
  • Liskov Substitution Principle
  • Interface Segregation
  • Dependency Inversion Principle

Still, I am going to revise some of the concepts :).

Note: I am going to use the kotlin and java syntax, but you can relate these examples with any OOP language.

How to create an Object:

class NameOfAClass{}
val objectOfAClass = NameOfAClass()

In the above code, first I created a class with the name “NameOfAClass” and after that, I created an object from this class.

Inheritance:

open class Parent{}
class Child : Parent(){}

// In Java
public class Parent{ }
public class Child extends Parent{}

In the above code, Parent is a class and Child is also a class but that class also inherits the Parent.

Polymorphism:

I am not going to explain what is Polymorphism, for that you can ask Google. I am going to show you how we can create a Polymorphism scenario.

open class Parent{
open fun doSomething(){
println("Do Something")
}
}

class ChildOne: Parent(){
override fun doSomething(){
super.doSomething()
println("ChildOne Do Something")
}
}

class ChildTwo: Parent(){
override fun doSomething(){
super.doSomething()
println("ChildTwo Do Something")
}
}

fun main() {
var parent: Parent = ChildOne()
parent = ChildTwo()
}

// In Java

public class Parent{ 
    void doSomething(){
        System.out.println("Do Something");
    }
}
public class ChildOne extends Parent{
    void doSomething(){
        super.doSomething();
        System.out.println("ChildOne Do Something");
    }
}

public class ChildTwo extends Parent{
    void doSomething(){
        super.doSomething();
        System.out.println("ChildTwo Do Something");
    }
}

public static void main(String[] args) {
    Parent parent = new ChildOne();
    parent = new ChildTwo()
}

In the above code, we have three classes, Parent, ChildOne and ChildTwo. ChildOne and ChildTwo are both inherit from Parent class. Now, where is polymorphism? We can see Polymorphism in the main method.

public static void main(String[] args) {
    Parent parent = new ChildOne();
    parent.doSomething()
   
    parent = new ChildTwo()
    parent.doSomething()
}

Basically, in this code, everything in front of us, that is why we can see, and we can predict Polymorphism behavior like when a parent.doSomething() called the first time, we will see the output of ChildOne implementation and when the second time parent.doSomething() called we would see the ChildTwo implementation. So the Parent is showing us a Polymorphic behavior. You need to remember this, instead, here I can see a Factory Pattern use case, but for now, if you are not able to see a Factory Pattern use case, it’s Okay. You will see one-day :).

Before going to the next revision topic. I am going to change something in the above main method code so we can see a unpredict Polymorphism behavior.

public static void main(String[] args) {
    Parent parent = getInstanceOfChild();
    parent.doSomething()
}

Now, anyone can tell me what will be the output of parent.doSomething() with your current knowledge? I know, No one can give me the answer until we see the implementation of getInstanceOfChild(). We can say, parent is still showing a Polymorphism, but from the perspective of the main method, I introduce an abstraction. Like you only know the interface of the getInstanceOfChild(), but you are not aware of the implementation of getInstanceOfChild(). 

Composition:

Again, I am not going to explain what is Composition you can consult from Google. Instead, I am going to show you how to achieve Composition in our above inheritance example.

class Parent{
    open fun doSomething(){
        println("Do Something")
    }
}

class ChildOne(val parent: Parent){
    fun doSomething(){
        parent.doSomething()
        println("ChildOne Do Something")
    }
}

class ChildTwo(val parent: Parent){
    fun doSomething(){
        parent.doSomething()
        println("ChildTwo Do Something")
    }
}

fun main() {

    val parent = Parent()
    val childOne = ChildOne(parent)
    val childTwo = ChildTwo(parent)
    
    childOne.doSomething()
    childTwo.doSomething()
}

// In Java 

public class Parent{
    void doSomething(){
        System.out.println("Do Something");
    }
}
public class ChildOne{
    private Parent parent;
    public ChildOne(Parent parent){
        this.parent = parent;
    }

    void doSomething(){
        parent.doSomething();
        System.out.println("ChildOne Do Something");
    }
}

public class ChildTwo{

    private Parent parent;
    public ChildTwo(Parent parent){
        this.parent = parent;
    }

    void doSomething(){
        parent.doSomething();
        System.out.println("ChildTwo Do Something");
    }
}

public static void main(String[] args) {
    Parent parent = new Parent();
    ChildOne childOne = new ChildOne(parent);
    ChildTwo childTwo = new ChildTwo(parent)
}

In the above code, we refactor our inheritance code to composition: Elementary, no rocket science. Try to compare both if you are not feeling comfortable with this code spend some time.
Now, the next questions are what we lose and what we gain when we change to composition?

What did we lose?

  1. Lose Flexibility -> If I want some method or filed available for ChildOne and ChildTwo, I can add that field or method in Parent class, and then I will get that in all of our inherit classes from Parent. 🙂
  2. Polymorphism -> In our main method, I am not able to enjoy polymorphism because ChildOne now did not inherit from Parent. So If I do a try like Parent parent = new ChildOne() that will not compile.
  3. No more abstraction. I am not able to achieve abstraction; instead, I need to write a concrete code.

What did we achieve?

  1. Gain Flexibility -> If I want to add something in Parent that will not impact my ChildOne and ChildTwo classes. So Parent code is the independent, or maybe a more appropriate case will be If I remove something which is not used in ChildOne and ChildTwo there will be no impact on my code. But in the case of Inheritance, mostly removal of code is painful.
  2. Specific functionality -> In case of composition, if I want to add some functionality that is only specific to ChildTwo but not available to ChildOne, I can add easily by giving that new functionality as a constructor parameter to ChildTwo. In the case of inheritance, we mostly need to add a specific method in Parent class to achieve the Liskov Substitution Principle, and then we need to remember don’t call that method in ChildOne but do override in ChildTwo from where mostly we start losing our control on Inheritance code.

As you can see, Inheritance and Composition are compelling concepts. Still, both can make our code amazing, and both can make our life hell, especially Inheritance, if we do design our classes carelessly. But in the case of Composition, we lose a concept of Polymorphism, which is the foundation of scalable code. I will discuss more this in later posts.
In conclusion, we need to use both; instead, some Design Patterns are using both concepts, and they give us a fantastic power like I will show you this power in the Decorator Pattern post.

Interface:

I am not going to explain to you what an interface is for that you can consult with Google. Instead, I am going to use this interface directly in our code.

I am going to refactor our Inheritance example by using an interface.

interface Base{
    fun doSomething()
}

open class Parent: Base{
    override fun doSomething(){
        println("Do Something")
    }
}

class ChildOne: Parent(){
    override fun doSomething(){
        super.doSomething()
        println("ChildOne Do Something")
    }
}

class ChildTwo: Parent(){
    override fun doSomething(){
        super.doSomething()
        println("ChildTwo Do Something")
    }
}

fun main() {
    var base: Base = Parent()
    base.doSomething()

    base = ChildOne()
    base.doSomething()

    base = ChildTwo()
    base.doSomething()
}

// In Java

public interface Base {
    void doSomething();
}

public class Parent implements Base {
    @Override
    public void doSomething() {
        System.out.println("Do Something");
    }
}

public class ChildOne extends Parent {
    public void doSomething() {
        super.doSomething();
        System.out.println("ChildOne Do Something");
    }
}

public class ChildTwo extends Parent {
    public void doSomething() {
        super.doSomething();
        System.out.println("ChildTwo Do Something");
    }
}

public static void main(String[] args) {
    Base base = Parent();
    base.doSomething();

    base= new ChildOne();
    base.doSomething();

    base = new ChildTwo()
    base.doSomething();
}

The above code is the same as the Inheritance one, only we got one more layer of Abstraction, and in the main method, now Base is the one who is showing the Polymorphic behavior. In this example, I am not able to show you a real power of Interface, but trust in me, interfaces are a fantastic concept, and that can make your code scalable.

Instead, I can show you some power of Interface. Take an example. You are a UI Engineer of some SDK. Now you created a Button class. You are also aware of your Button class will be used by thousands of developers.

Now, you are thinking about how to implement the logic of Button Click because you don’t know what you should do when button click. Like one developer wants when a button is click to execute the logic of logout, other developers want to write the execute login logic on button click. Some other developer wants to start a checkout process when button click. Now, this is the one use case of an interface. When you are not aware of the implementation, but you know you can create some abstraction and provide that abstraction layer to your users. Later you can call their concrete implementations by using your given abstraction as we can see in the below example.

interface Click {
    fun onClick()
}

class Button {

    private var clickListener: Click? = null

    fun setClickListener(click: Click) {
        clickListener = click
    }

    fun onDrawOrSomething() {
        // A lot of Code related to Button UI or internal logic
        clickListener?.onClick()
    }
}

So in the above example, Click interface is in abstraction for your code. You don’t know what developers will implement against this, but now they can use it, and your code is independent of there concrete Implementation.

Now, we are ready to move to the Adapter Design Pattern.

Adapter Design Pattern:

Before going to start a discussion about the Adapter Design Pattern, I am sharing with you one crucial point about Structural Design Pattern. These Patterns are not as simple as Creational Design Patterns. If you are new in the OOP or Design Patterns world, you need to do a lot of practice, thinking, observation, and try to observe existing code and ask questions to your self.

  • Why you are using inheritance, are you able to refactor your current code to Composition? ( vice versa also a good question )
  • Why you are using a base class, are you able to create an interface from base class methods and then implement that interface into your base class?
  • Why you are using Concrete classes as reference objects, why not change references to Base or Abstract classes or maybe to interfaces?
    interface Parent{}
    class Child implements Parent{}
    public static void main(String[] args) {
    Child child = new Child();
    // to
    Parent parent = new Child();
    }

Ask these questions and do some refactoring for practice in your spare time. That will be helpful for you. Otherwise, you will face the problem, in the case of the Structural Design Pattern.

Now, I am going to start a simple example where I will show you how the Adapter Design Pattern will help us. One thing, don’t focus on the names of classes. Instead, try to grab the concept so you can use it in your code.

Notable: This example, will help you to grab the concept of the Adapter Design Pattern, but that is not a proper implementation of the Adapter Design Patter. Instead, that is a Data Structure problem which we should resolve by using Mapping. So please bear with me, but I have a feeling this example will help most of my readers.

For example, we are writing an application, in which we have three teams: backEnd, FrontEnd, and Design. Now, after a lengthy discussion, everything is resolved, and currently, teams are working on implementation in Parallel. The backend team is creating the APIs for the FrontEnd, and meantime, FrontEnd team is working on the Application logic by the following design. After two weeks backend is completed his task and FrontEnd is also ready with MockData. Now, FrontEnd needs to integrate Backend by replacing Mock implementations.

When teams reviewed APIs, they find out a big issue in Data Structures, as shown below:

// Backend Data Structure
class ObjectFromRemote {
    public String id;
    public String title;
    public String description;
    public Integer count;
    public Float price;
    public Integer followers;
    public String url;
    public String metaData;
    public String referenceUrl;
}
// Frontend Data Structure 
class UIObject {
    public String id;
    public String title;
    public String description;
    public String price;
}

[Again, this is a Mapping problem between the Data Structures. For now, to grab the concept, I am going to resolve this with the Adapter Design Pattern analogy, which is not the right choice. So don’t use this approach if you face this use case in your task.  Adapter Design Pattern always used in those use cases where we have Interfaces, inheritance, and composition between classes]

(By the way, if you are interested to know how you should fix this problem. Try to review my other post in Good Coding Practices. Oh no Is it Map function? ( LOL on Complication ) )

Now here we have a question? What is the problem which we need to resolve, basically our app is dependent on the UIObject as shown in the below code and what we are getting from the backend that is a different object.

public static void uiRender(UIObject uiObject) {
    // UI render logic
}
UIObject != ObjectFromRemote. :(

Now, as we know the general definition of the Adapter Design Pattern, if you have two different interfaces and you want to communicate between these two, you always create an Adapter Class, and your problem will be resolved.
So, in this case, we will see how the Adapter Design Pattern will fix our problem. ( Remember this example is not a proper implementation of ADP … 🙂

First step:

Find out the dependency. Our Application UI is dependent on UIObject, and our UIObject needs to be dependent on ObjectFromRemote class information. So it’s mean we need to create an Adapter class that will Adapt the ObjectFromRemote and will return the UIObject class object.

There are two approaches that we can use from the perspective of the Adapter Design Pattern.

  1. By Using Inheritance
  2. By Using Composition

We will discuss both.

By Using Inheritance:

class ObjectFromRemoteAdapterByInheritance extends ObjectFromRemote {
    public UIObject uiObject() {
        UIObject uiObject = new UIObject();
        uiObject.id = this.id;
        uiObject.title = this.title;
        uiObject.description = this.description;
        uiObject.price = "Price is " + this.price;
        return uiObject;
    }
}

In the above code, we created a new Adapter class, which is extended by the ObjectFromRemote and contains one method, which will return the UIObject. It’s time to see how we can use this code in our main application.

public static ObjectFromRemoteAdapterByInheritance getDataFromBackground(){
    // Async logic. Assume we are using Coroutines so I am allowed to return object as sequesntial code. No Callbacks
    ObjectFromRemote remoteObject = null; // Got this object from API 
    ObjectFromRemoteAdapterByInheritance adapter = new ObjectFromRemoteAdapterByInheritance();
    // Mapping code
    return adapter;
}

public static void main(String[] args) {
     ObjectFromRemoteAdapterByInheritance adapter =  getDataFromBackground();
     uiRender(adapter.uiObject());
}

So it is too simple, we can revise the steps.

  1. We already have an API that is going to provide the required functionality but whose interface is not compatible with our required implementation.
  2. Create a new class and do inheritance with the current incompatible class, which will be called the Adapter class.
  3. The adapter class will have one method that will return us the required object as per our application implantation.

By the way, the above definition is a proper Cheat Sheet Definition of an Adapter Design Pattern, which we will see in an appropriate case of use next.

By Using Composition:

class UiObjectAdapterByComposition{

    private ObjectFromRemote objectFromRemote;
    public UiObjectAdapterByComposition(ObjectFromRemote objectFromRemote){
        this.objectFromRemote = objectFromRemote;
    }

    public UIObject uiObject() {
        UIObject uiObject = new UIObject();
        uiObject.id = this.objectFromRemote.id;
        uiObject.title = this.objectFromRemote.title;
        uiObject.description = this.objectFromRemote.description;
        uiObject.price = "Price is " + this.objectFromRemote.price;
        return uiObject;
    }
}

Too simple, now we will see how we will use this code in our application.

public static UiObjectAdapterByComposition getDataFromBackground(){
    // Async logic. Assume we are using Coroutines so I am allowed to return object as sequesntial code. No Callbacks
    ObjectFromRemote remoteObject = null; // Got this object from API
    UiObjectAdapterByComposition adapter = new UiObjectAdapterByComposition(remoteObject);
    return adapter;
}

public static void main(String[] args) {

      UiObjectAdapterByComposition adapter =   getDataFromBackground();
      uiRender(adapter.uiObject());
}

Amazing, we fixed our problem.

Now it’s time to discuss a proper use case of Adapter Design Pattern. But before that, we can finalize the Cheat Sheet definition of the Adapter Design Pattern.

  1. We already have an API that is going to provide the required functionality but whose interface is not compatible with our required implementation.
    • ( Adapter Pattern by Inheritance implementation ) Create a new Adapter class and do inherit this class with the incompatible class and implement the compatible interface.
    • ( Adapter Pattern by Object or Composition implementation) Create a new Adapter class that will implement the compatible interface, and this class constructor or maybe the setter method will ask the incompatible class or interface.
  2. The adapter class will have one method that will return us the required object as per our application implementation.

We have current application which looks like as shown below.

interface One {
void one();
}

class FirstImpl implements One {

@Override
public void one() {
System.out.println("First Impl");
}
}

class SecondImpl implements One{

@Override
public void one() {
System.out.println("Second Impl");
}
}
class Factory{
    public static One createInstance(String s){
        if(s.equalsIgnoreCase("First"))
            return new FirstImpl();
        else
            return new SecondImpl();
    }
}

class Main {
    public static void main(String[] args) {
        One one = Factory.createInstance("First");
        one.one();
    }
}

Very simple, but a powerful example of grabbing the concept of the Adapter Design Pattern. We have one interface with name One and two implementations of this interface with the name FirstImpl and SecondImpl. Then we have a Factory who is responsible for creating the Concrete Object of FirstImpl or SecondImpl but return as One interface type.

After some time, we find out a new library which is doing the same work that we achieve in the FirstImpl class but with fantastic performance and we decided to move to that library. It’s time to review the new library APIs, as shown below.

interface Two {
    void two();
}

class Good implements Two {

    @Override
    public void two() {
        System.out.println("Good");
    }
}

Ah, that is not good news. If we want to move to this new library, we need to refactor our whole application code, which has thousands of lines of code.

But on a positive side, we can see the use case of the Adapter Design Pattern. As we know, if interfaces are not compatible, it’s mean we need an Adapter to resolve this issue. So now, we will write an Adapter class by using inheritance to fix this issue.

class GoodAdapterByInheritance extends Good implements One {
    @Override
    public void one() {
        two();
    }
}

Not a complicated process. Always remember, if you are going to implement the Adapter Pattern by inheritance. Your Adapter needs to extend the incompatible class and should implement your compatible interface, as shown in the above code.

Its time to see what are the changes we need to do in our application.

class Factory{
    public static One createInstance(String s){
        if(s.equalsIgnoreCase("First"))
            return new GoodAdapterByInheritance(); // Change
        else
            return new SecondImpl();
    }
}

class Main {
    public static void main(String[] args) {
        One one = Factory.createInstance("First");
        one.one();
    }
}

As per our above implementation, only we created a new Adapter class, and we changed the current Factory implementation. Now, all our codebase where we are using FirstImpl that will automatically start using our new library implementation. Fantastic, now it’s time to see how to implement this by using composition. Maybe at this point, you have a question, why you need an Adapter Pattern by Object or Composition, so in this case, assume GoodImpl is a Final class by Library, so you are not able to extend this. Also, the composition is more flexible because we can change things at runtime.

class NewLibraryAdapterByComposition implements One{
    private Two two;
    public NewLibraryAdapterByComposition(Two two) {
        this.two = two;
    }

    @Override
    public void one() {
        two.two();
    }
}

Not a complicated process, always remember, if you are going to use Adapter Pattern by Composition. Your Adapter class needs to implement your current application compatible interface, and as a composition or a constructor parameter, you will use the incompatible or library base class or interface.

Its time to see what are the changes we need to do in our application.

class Factory{
    public static One createInstance(String s){
        if(s.equalsIgnoreCase("First"))
            return new NewLibraryAdapterByComposition(new Good());
        else
            return new SecondImpl();
    }
}

class Main {
    public static void main(String[] args) {
        One one = Factory.createInstance("First");
        one.one();
    }
}

Only we did a change in our Factory class, where we replaced our FirstImpl class with NewLibraryAdapterByComposition class, and then we are giving the library class Good as a parameter.

I hope things are clear. Its time to review Adapter Pattern in the current SDK’s implementation.

Java SDK 1.8:

There is a very famous API BufferReader. Mostly indifferent code bases we can see the usage of this API as shown below:

BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));

Here I will say that is the use of the Decorator Design Pattern, but there is one more perspective. I can say we are using Object Adapter Design Patter or Adapter Design Pattern by Composition. Its time to explore the API.

public final class System {
    // Lot of code I remvoved to make things simple
    private System() {
    }

    /**
     * The "standard" input stream. This stream is already
     * open and ready to supply input data. Typically this stream
     * corresponds to keyboard input or another input source specified by
     * the host environment or user.
     */
    public final static InputStream in = null;

    ... 
}

What is InputStream?

public abstract class InputStream implements Closeable {
// A lot of code which can review in JDK
...
}

Now here we need to create a perspective of the Adapter Design Pattern. We have a class which is using System.in to give me back InputStream as shown in the above code. But my application is dependent on the BufferReader class.

public class BufferedReader extends Reader {
  ....
       public BufferedReader(Reader in) {
          this(in, defaultCharBufferSize);
       }
  ....
}

Now, InputStream and BufferedReader both are incompatible. It means we have a use case of Adapter Design Patter. We will see how JDK implemented this Adapter.

public class InputStreamReader extends Reader {
   ...
    public InputStreamReader(InputStream in) {
     ...    
    }
   ...
}

Now, we can see the class name in the above code doesn’t contain the Adapter but working as an Adapter. As we can see, InputStreamReader will take the InputStream object as a parameter, and then InputStreamReader passing itself as a parameter to BuffereReader, and both are extending from Reader class.

BufferedReader bufferedReader = new BufferedReader(
// Here InputStreadReader working as a Adapter Class between // two incompaitable interfaces 
new InputStreamReader(System.in)
);

Retrofit:

There is a very famous library for networking, which is called Retrofit. In the Android community, this library is a default standard for network API. In this library, we can see the proper use of the Adapter Pattern. Instead, I will say this library implementation is using Factory, Builder, Adapter, and other Design Patterns together. But I will try to show you only the Adapter Pattern.
https://github.com/square/retrofit/blob/master/README.md

This library support, Call, RxJava, RxJava2, and more incompatible types of API’s to use for network calls amazingly.

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://example.com/")    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    .build();

In the above code, the addCallAdapterFactory method is the one who is managing incompatible API’s issue. Here if we give the RxJava2CallAdapter, our Retrofit object will start working with Observables if I provide any other Adapter Retrofit start working with that Adapter.

By the way, here you can see the library is using Builder Pattern, Factory Pattern, and Adapter Design Pattern.

public interface CallAdapter<R, T> {    
    ...
    
    abstract class Factory {
        ...
    }
}
https://github.com/square/retrofit/blob/master/retrofit/src/main/java/retrofit2/CallAdapter.java
public final class Retrofit {

    ...
        /**
         * Add a call adapter factory for supporting service method return types other than {@link
         * Call}.
         */
        public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
            callAdapterFactories.add(Objects.requireNonNull(factory, "factory == null"));
            return this;
        }
        
    ...
}
https://github.com/square/retrofit/blob/master/retrofit/src/main/java/retrofit2/Retrofit.java

In the above code, the callAdapterFactory is going to accept any class or object who is extending by the CallAdapter,Factory interface.

final class RxJava2CallAdapter<R> implements CallAdapter<R, Object> {
   ...
}
https://github.com/square/retrofit/blob/master/retrofit-adapters/rxjava2/src/main/java/retrofit2/adapter/rxjava2/RxJava2CallAdapter.java
public final class RxJava2CallAdapterFactory extends CallAdapter.Factory {
 ...
}
https://github.com/square/retrofit/blob/master/retrofit-adapters/rxjava2/src/main/java/retrofit2/adapter/rxjava2/RxJava2CallAdapterFactory.java

Conclusion:

In conclusion, we will repeat what we learn today.

Q: Where should we use Adapter Design Patter?
Ans: Anywhere we can see the two different or incompatible API’s we can create an Adapter class to resolve the problem.

Q: How many approaches we have to implement Adapter Design Patter?
Ans: We can implement by Inheritance or by Object or Composition.

Q: What are the steps we need to use to implement the Adapter Design Pattern by inheritance?
Ans: Your Adapter needs to extend the incompatible class and should implement your compatible interface.

class InCompatible{ }

interface Compatible{ }

class InCompatibleAdapter extends InCompatible implements Compatible{ }

Q: What are the steps we need to use to implement the Adapter Design Pattern by Object or Composition?
Ans: Your Adapter needs to implement the compatible interface, and this class constructor or maybe the setter method will ask the incompatible class or interface.

class InCompatible{ }

interface Compatible{ }

class CompatibleAdapter implements Compatible{
private InCompatible inCompatible;

public CompatibleAdapter(InCompatible inCompatible) {
this.inCompatible = inCompatible;
}

// OR

public void setInCompatible(InCompatible inCompatible) {
this.inCompatible = inCompatible;
}
}

More Posts on Design Patterns https://www.uwanttolearn.com/design-patterns/

Facebooktwitterredditpinterestlinkedinmailby feather

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.