0

I got a bit strange 'future ready' scenario. And I'm not sure if I got it right in regards to C# adapter design pattern.

The scenario is that to be future ready, 'to use the adapter pattern' to easily switch from underlaying frameworks.
As for example here I use an instruction to move a robot arm vendorXYZ by 10cm left.
Thus vendors can in the future be different.

It's possible to do this with the adapter pattern, no doubt.
But since we currently have only one robot case.
Why not simply write out interfaces is there a problem with such an approach?.

Interface RobotControl {
 public boolean overwriteable Move_10_cm_left()
}
Class vendorX : RobotControl {...}
Class vendorY : RobotControl {...}

So what is the added value here of using the adapter model when dealing with future changes.
If there is nothing to match at currently why not use simple interfaces?.
For this scenario to me the pattern feels alike synthetic sugar, but please prove me wrong.

2
  • The method is probably called Move_10_cm_left to illustrate something that is likely to change in the future, but I just can't get over the name. Why not move(direction : Direction, distance : Distance)? Then you can have a Direction enum (left, right, up, down, whatever you need) and a Distance type (which takes a number and unit of measure). No adapter needed. Feb 4, 2022 at 19:31
  • 2
    I'm confused. Who owns the vendorX and vendorY classes? If you own them, they are the adapters. If you do not own them, you obviously cannot add an interface to them. Am I missing something?
    – John Wu
    Feb 6, 2022 at 3:29

4 Answers 4

1

What you're describing is the Adapter pattern (a way it can be realized). The interface (that your client code is written against) allows you to insert, if the need arises, an implementation that adapts the vendor-provided API (functions they provide) to what's expected by your client code.

E.g., your own (client) code calls Move_10_cm_left(), but vendorX may provide a function Move(direction, amount), while vendorY may have something like send(actuatorID, controlSignal) (I'm making these up, but you get the picture). The assumption is that you have no means/desire to change the source code of the vendor-provided functions; instead, you'll just write a special-purpose wrapper around them.

So, on your side, you write code that expresses some complex behavior of the robot in terms of your interface. Then, when you have to change the vendor, you can leave that code as is (or change it minimally - there could be unforeseen reasons for it to change), and only write an adapter that implements that interface:

// Your RobotControl interface is the (abstract) Adapter

// This is a ConcreteAdapter, written by you
class VendorYControl : RobotControl {
  public override bool Move_10_cm_left() {
      send(xyzID, signalSpec);   // this is the vendor-provided API
  }

  // ... other methods ...
}

Then you inject that into your client code; something along these lines:

RunControlLoopWith(new VendorYControl());

// Where the signature of the function is
public void RunControlLoopWith(RobotControl control) {
  // do stuff using 'control'
}

So now you have a library that supports two vendors, with an adapter for each. The code that defines the core behavior for both is the same, and you can inject more adapters in the future.

The interface allows you to keep the different adapter implementations, and to inject one of your choosing. If you had a concrete class instead, you'd still be able to change the implementation behind the public methods & properties, by changing the "guts" of the class, but you'd then be throwing away the old implementation.

Patterns can be realized in a number of different ways; it's not the exact details of their UML diagram that define them, they are about the intent, and the roles and interrelationships of the elements that comprise them.

So here are some variations. If there's some high-level behavior shared by all adapters, you can turn the interface into an abstract class. Alternatively, you could keep the interface, and use the Decorator pattern to transparently add that behavior on top. If you're using a duck-typed language like JavaScript, or more likely, Python, you may not have an explicit abstract adapter (interface or class) defined anywhere in the code - it could all just be defined in the specification/documantation for the RunControlLoopWith method and its control parameter. It's still the same pattern.

1

YAGNI?

A provocative preliminary question about the future whether you're really going to need it. Why not start with Vendor and refactor with RobotControl when the future will be present?

(Sorry, I had to ask)

How does the adapter work?

Let's assume that you'll really need it. Adapters aim to convert the interface of an adapted class (Adaptee) into another interface (Target). There are three main families of this pattern:

  1. Inheritance-based adapters: Adapter inherits from Adaptee and from Target. This design originally proposed by GoF would require multiple inheritances, which is not supported by C#.
  2. Realization-based adapters: This is the variant of 1 based on interfaces instead of inheritance: Adatper inherits from the Adaptee to reuse its implementation and implements Target interface
  3. Composition-based adapters: the Adapter implements the Target interface, and is composed (i.e. object composition) of an Adaptee.

All the cases share a common intent: converting the interface. For this purpose, Adapter always fulfils Target interface by translating its operations into operations of the Adaptee.

Why is it relevant in your case?

I understand that in your design, the Adaptee is RobotArmVendorXYZ (I rename it, as "vendor" has a strong accounting/purchasing connotation), and I suppose that every vendor provide its own interface for its robot arm. For example:

  • VendorX may offer an interface that allows to orient arm according to 3 relative angles;
  • VendorY may offer an interface designed around the position of end of the arm that you can move in absolute distance according to 3 orthogonal axes

Your Adaptor would then allow the clients to ask for a 10 cm left move, but this move would be implemented very differently depending on the vendor's API. It's really a conversion of interface and the pattern is relevant.

What's the issue with your more compact design?

Your simpler design would almost be an adapter. It's just that you modify the vendor's classes to offer the adapted behavior. While this may seem a nice shortcut, in practice, it has the problem that:

  • It does not comply with the Single Responsibility Principle (your vendor class has 2 reasons to change: 1-the API of the vendor + 2-the use of the API for your own purpose)
  • It is not in the spirit of the Open Closed Principle, since you'd always need to change your vendor class if you want to add new common capabilities.

Conclusion/Recommendation

Go the extra-mile: split the challenges using a real adapter pattern. VendorA class wpuld implement the vendor API and only the vendor API. And RobotArmVendorA would extend the VendorA and implement the common RobotArm interface (so it's the second kind of adapter family).

0

Is there really a difference between the two? The interface is the interface implemented by the adapter. In this case class vendorX and class vendorY are the adapters.

The pure interface approach and the adapter approach are one and the same.

Use Dependency injection and a IoC Container to select the vendor at a single place (startup code) in your code.

3
  • its what keeps me puzzled, i hope someone here can see a benefit one over the other. I wonder for example should an adapter be the configurable switch between the to be used robot-vendor. (would that ease coding, instead of adjusting calling functions for each motor instruction)... (perhaps then you can switch adapter once centrally.)
    – Peter
    Feb 4, 2022 at 16:41
  • What a tried to say is: the pure interface approach and the adapter approach are one and the same. Use dependency injection (DI) to select the vendor. Feb 4, 2022 at 16:44
  • 1
    @Peter: The purpose of the adapter patter and interfaces is to not have to write switching code. You can write your handling code against that interface, without needing to account for which implementation of the interface you're working with. At a different location, your application can be configured to use any valid implementation of the interface. Whether this is an application-level configuration, situational choice based on certain parameters, user-configured setting, ... is highly contextual and not addressed in your question.
    – Flater
    Feb 7, 2022 at 9:22
0

Thus vendors can in the future be different.

Then contain knowledge of the vender. Don't let it leak.

But, what are you going to adapt to and from if you use an adapter now?

Unless you see a clear proprietary gotcha in the interface the vender is providing then talk to the current interface. Do so in a way that keeps your code from being able to tell which vender it's talking to.

It's only if you can't do this that you'd create an adapter to translate your vender agnostic interface to a vender specific one.

Why not simply write out interfaces is there a problem with such an approach?. Keep in mind, the benefit of using big I Interfaces is that it gives support of multiple inheritance that abstract classes can't in these Java like languages.

So long as the design will allow adapters to be added with minimal hassle later you're fine not implementing them now. But if you have access to the interface of a potential other vender it might be worth taking a look at it now. Just so you can check how much work you'd be setting yourself up for.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.