I am reading chapter four and it doesn't make sense to me can you clarify what you mean... What is mean by a pass-through interface? Also you state - "Implement the new interface by simply calling the old one". Can you give an example of what you mean?
Examples are always the hardest part. Do you have a concrete example to share?
I've never heard "pass-through interface", but I would assume it would be something like the Facade design pattern. Which in my interpretation is "only an interface", a Facade does not do any implementation, it's just another interface you can couple to, e.g. because you don't want to couple to the interface behind it, or because the interface behind it doesn't work for your use-case.
When talking about an interface in this comment, I mean interface as a concept, not the language construct that some languages have. Everything has an Interface. The interface of a function is it's name, it's arguments and it's return types. The interface of a TV is it's remote control, and the interface of a car is it's steering wheel, gas/break pedals, etc. When using it like intended, we don't directly interact with the internals of a TV (circuitry), nor with the internals of a car (engine).
We agree. I too mean "interface" in the abstract sense--how you invoke logic--not the language construct. I need to be more clear about that. And yes, I'm talking about a Facade. Some facades seem to inserted early. I'm talking about putting one in later when you realize what interface you actually want.
Bran, I was thinking this very same thing... and thinking about Facade as well. Where I've seen that come into practice in code, especially already-written-code is one of two places:
(1) when there's a gem/library that is an API to some external service and I want to have more control over usage/naming... so I might wrap that API with my own class/module, even if it's just pass-through. I've seen this in changing method names, for example, because the gem/library has methods in a passive tone, i.e. "perform_calculation()" but I prefer more active tone, i.e. "calculate()". So I can wrap the former call in my own facade with the latter naming and makes my code cleaner (at least to me).
And (2) when I have multiple, similar systems and I want to wrap them in a consistent interface... and perhaps one or more of them are already laid out well. So I still build the Facade but for at least one system (or module/api/library) it's already set pretty much the way I want it to be. So I wrap it as a pass-through, but that then defines the pattern _in the rest of my code_. Using the example from (1), I might have an area calculator library and a volume calculator library and I want to just use "calculate()" on both... maybe one of them already has a "calculate()" method but I still want to have my own layer of code on top of them for consistency across my codebase. So MyAreaClass.calculate() might end up just calling SomeLibrary.calculate() even with the same params and whatnot.
Possible (3): on occasion the pass-through allows for easier testing/stubbing. But that's not the first reason I'd reach for Facade, even if it comes in handy.
What I'm struggling with a lot in my daily work is classes with only static methods (doubling the dependency is a pain in testing), and very often, the interface of these methods are huge or overloaded multiple times.
What I do then is creating a new class, move some of the parameters into the constructor of the new class and create a method which calls then the method I need.
Using shapes (as always) as an example:
```ShapeSizeCalculator.calculateArea(Shapes.SQUARE, new Point(0,0), 4)```
could be changed to:
```new Square( new Point(0,0), 4).calculateArea();```
Their Code returns a nullable integer, but I prefer to return Maybe Integer, so I write an Adapter that provides the same behavior with a different data format.
If we do this kind of thing enough, then eventually we bump into behavior of Theirs that we consider annoying or repetitive and that we hide behind a more-interesting Adapter that is probably more involved than a Pass-Through.
"The line" is somewhere in between these two things.
What if we build an Adapter that catches exceptions and turns them into Either.left values? What if we publish 3 independent interfaces that break apart an annoyingly-large interface of Theirs? What if we publish a single interface that unifies an annoyingly-fragmented collection of interfaces of Theirs? Those all sound quite Pass-Throughish to me.
What if we take a collection of custom-built parsers and hid them behind a unifying interface so that we could compose them in the style of Parser Combinators? Is that Pass-Throughish? I'm not entirely sure.
Those are just some ideas that sprang to mind. I hope something helped.
> While the modern GNU implementation of bc(1) is standalone, the classic version passed commands to dc(1) over a pipe. In this division of labor, bc(1) does variable substitution and function expansion and translates infix notation into reverse-Polish — but doesn't actually do calculation itself, instead passing RPN translations of input expressions to dc(1) for evaluation.
At least to me, a "pass-through" interface would wrap some other interface, but present an identical one. I'd consider your example to be a fairly advanced form of one object implementing it's behaviour in terms of another. But as others have said, it's fascinating to see the many interpretations people have of the original question.
If it's exactly the same interface, maybe v0 should call the original interface, but if future versions also do, then I'm struggling to see why implement a 2nd interface at all?
An example would be to add logging, or statistics, or perrformance measurement, and so on. The apparent functionality for the callers doesn't change at all, but extra side-effects have been added. This sort of pass-though could also massage arguments and/or return values, I suppose.
I asked Chat GPT and here is what I got: A "pass-through interface" in programming or system design refers to a type of interface that transparently passes data or commands from one component to another without modifying them. This term is often used in contexts where data needs to be routed or transferred between different layers or parts of a system, but the intermediary doesn't need to (or shouldn't) alter the data in any way.
For example, consider a computer system where you have an application that needs to access a database. If the application communicates with the database through a pass-through interface, it means that the commands it sends (like SQL queries) are passed directly to the database without any alteration, and the responses from the database (like query results) are returned back to the application as is.
This kind of interface is useful in situations where the integrity of data or commands is paramount, and where the intermediary's role is simply to facilitate communication rather than process or analyze the data. It's a way of ensuring that the data remains consistent and unchanged as it moves through different parts of a system.
Applying the concept of a "pass-through interface" to everyday life and work situations involves adopting a mindset of transparency and direct communication, where information is relayed accurately and without unnecessary modification. Here are some examples of how this can be applied:
Clear Communication in Workplaces: In a professional setting, acting as a pass-through interface means conveying messages or instructions from one party to another without adding personal bias or alterations. For instance, if a manager relays a directive from senior leadership to their team, presenting it as it was originally intended ensures clarity and consistency.
Information Sharing: When sharing information with colleagues or friends, try to present facts and data as they are, especially in critical scenarios like medical advice, legal information, or technical instructions. This helps to prevent misunderstandings and misinformation.
Mediation and Conflict Resolution: In situations where you might be mediating a discussion or conflict, acting as a pass-through can be beneficial. By neutrally conveying each party's perspective to the other without adding your interpretation, you can help facilitate a clearer understanding and potentially resolve conflicts more effectively.
Education and Training: As a trainer or educator, conveying information accurately and in its original context is vital. While interpretation and elaboration are part of teaching, ensuring that the core information is passed through correctly is crucial for accurate learning.
Project Management: When managing projects, acting as a pass-through for client feedback or upper management directives ensures that the team receives unaltered and clear instructions, leading to better alignment with the project's goals.
Personal Relationships: In personal relationships, being a good listener often means absorbing what the other person is saying without immediately interpreting or judging. This can be seen as a form of pass-through communication, where you allow the person to express themselves fully before responding.
In essence, using a pass-through interface approach in life and work is about ensuring that communication remains as unfiltered and undistorted as possible, which is crucial for maintaining transparency, trust, and clarity in various scenarios.
This doesn't help on the example front, but it's possible I'd describe such interface as a proxy, or maybe facade or adapter, although I think that would be stretching the usual implications of the last two. Even for "proxy" I'd be most likely to use it if there was an element of forwarding api requests elsewhere (another thread, process, node, etc). Also, perhaps "wrapper"?
One possible example that popped into my head: sometimes it's useful to track the operations performed via an interface (possibly logging, possibly recording statistics, etc). This might be a good motivating example for the idea.
I used it already a couple of days after I read the chapter when I had to change a small java application I wrote 3 years ago. The app watches a directory for new files, does some transformations and then writes into kafka.
We are currently migrating to kubernetes and the java WatchService did not work inside a container. Therefore I created a new "FileDetector" interface and a "WatchingDetector" implementation by mostly copying the old code. Tests were still passing.
I could then easily write an alternate "PollingDetector" Implementation and replace the old one while tests were still passing.
I think this is reasonably common use a "pass-through" interface, especially initially to introduce the abstraction layer. I might expect that abstraction to move away, at least a bit, from a straight pass-through model as the new version is implemented, since that new version might bring along some new features / behaviour.
A bit unrelated examples that could fall in there.
Linux WINE implementing windows API over Linux API to run windows applications on linux without emulation. Or also, any wrapper around basic C/posix library API to provide easier to use API.
In these case, you are never going to get rid of the old one, but your application can use the new one everywhere and be easier to understand.
Also thinking about the python random API, that provides different high level functions just implemented over the low level random -> [0, 1[ to simplify calling code. `randrange`, `choice`, `choices` that are more expressive than seeing the actual arithmetics (+ with low level implementation possibles)
So any "helper" function to use your API could fall as example. "pathlib.write_text" instead of having to do "with open(path, mode=whatmodeisrequiredagain?) as fd: fd.write()". The code executed is actually the same, but the reader understands better what is important. And programming is anyway just doing passthrough over basic CPU operations.
Going back to the context.
I imagine that in the case of that chapter, it was more, design the API you would like to have to use in your application. Write it using your old legacy API. Then you can in parallel migrate your users to the new API to improve their code, and implement the new interface without relying on the old interface. It would anyway be based on another interface, kernel/library/low level something, and so providing a "passthrough" again, but not anymore your own corporate API.
My mind lept less to code structures and patterns and more to what ChatGPT referenced with its SQL example. In many cases there is a need to support a "newer" type of interface such as REST or SOAP. Organizations are littered with systems that wrap legacy interfaces in order to present them to new clients using a different protocols.
A pass-through interface in the context of programming languages refers to an interface that simply delegates method calls to another object. It acts as a transparent layer, forwarding calls to the underlying implementation without adding any additional functionality. This pattern is often used to provide a consistent interface while allowing flexibility in the actual implementation.
PassThroughInterface proxyA = new PassThroughProxy(new ImplementationA());
PassThroughInterface proxyB = new PassThroughProxy(new ImplementationB());
proxyA.performAction(); // Output: ImplementationA is performing the action.
proxyB.performAction(); // Output: ImplementationB is performing the action.
}
}
```
In this example, `PassThroughInterface` defines a method `performAction()`. `ImplementationA` and `ImplementationB` provide concrete implementations of this interface. The `PassThroughProxy` class acts as the pass-through interface, taking an instance of `PassThroughInterface` during its construction and delegating the `performAction()` call to the underlying implementation. The `Main` class demonstrates how you can use the pass-through proxy with different implementations.
In my mind, pass-through is a general thought approach, while patterns like adapter and facade are recipes for implementing the pass-through concept in different situations.
My example for a pass through:
We had apis used by our customers. We were committed to supporting old versions of each api for several years. When a new version of an API is published, a pass through is created to expose the old version, and convert it on the back end to the new version, providing appropriate defaults, etc.
So finally reading the chapter in question (which is to say, page 11)... It's clear that a "pass-through interface" as used in the text, does not mean *just* an interface that preserves exactly the interface of the wrapped objects which was my interpretation of the fragment starting off this post.
So I think the concepts like "facade" and "adapter" and "decorator" would all be possible synonyms for the concept being discussed. To me, the first two especially have a connotation of modifying or extending the wrapped object's interface somehow. A "decorator" can certainly do the same, I think. Again, just for me personally, "proxy" is too strongly connected in my brain with forwarding requests over some sort of communication link.
To me personally though, the phrase "pass-through" is a synonym for "transparent" which implies a drop-in replacement that may add some desired side effects, eg, logging, perf measurements, maybe caching, etc, but which requires no or negligible change to the existing clients of the wrapped object.
I've never heard "pass-through interface", but I would assume it would be something like the Facade design pattern. Which in my interpretation is "only an interface", a Facade does not do any implementation, it's just another interface you can couple to, e.g. because you don't want to couple to the interface behind it, or because the interface behind it doesn't work for your use-case.
When talking about an interface in this comment, I mean interface as a concept, not the language construct that some languages have. Everything has an Interface. The interface of a function is it's name, it's arguments and it's return types. The interface of a TV is it's remote control, and the interface of a car is it's steering wheel, gas/break pedals, etc. When using it like intended, we don't directly interact with the internals of a TV (circuitry), nor with the internals of a car (engine).
We agree. I too mean "interface" in the abstract sense--how you invoke logic--not the language construct. I need to be more clear about that. And yes, I'm talking about a Facade. Some facades seem to inserted early. I'm talking about putting one in later when you realize what interface you actually want.
Bran, I was thinking this very same thing... and thinking about Facade as well. Where I've seen that come into practice in code, especially already-written-code is one of two places:
(1) when there's a gem/library that is an API to some external service and I want to have more control over usage/naming... so I might wrap that API with my own class/module, even if it's just pass-through. I've seen this in changing method names, for example, because the gem/library has methods in a passive tone, i.e. "perform_calculation()" but I prefer more active tone, i.e. "calculate()". So I can wrap the former call in my own facade with the latter naming and makes my code cleaner (at least to me).
And (2) when I have multiple, similar systems and I want to wrap them in a consistent interface... and perhaps one or more of them are already laid out well. So I still build the Facade but for at least one system (or module/api/library) it's already set pretty much the way I want it to be. So I wrap it as a pass-through, but that then defines the pattern _in the rest of my code_. Using the example from (1), I might have an area calculator library and a volume calculator library and I want to just use "calculate()" on both... maybe one of them already has a "calculate()" method but I still want to have my own layer of code on top of them for consistency across my codebase. So MyAreaClass.calculate() might end up just calling SomeLibrary.calculate() even with the same params and whatnot.
Possible (3): on occasion the pass-through allows for easier testing/stubbing. But that's not the first reason I'd reach for Facade, even if it comes in handy.
What I'm struggling with a lot in my daily work is classes with only static methods (doubling the dependency is a pain in testing), and very often, the interface of these methods are huge or overloaded multiple times.
What I do then is creating a new class, move some of the parameters into the constructor of the new class and create a method which calls then the method I need.
Using shapes (as always) as an example:
```ShapeSizeCalculator.calculateArea(Shapes.SQUARE, new Point(0,0), 4)```
could be changed to:
```new Square( new Point(0,0), 4).calculateArea();```
The method signature tells me that :
Shapes.SQUARE should have a method area that gets a Point and an Integer.
I can think of one microexample to start:
Their Code returns a nullable integer, but I prefer to return Maybe Integer, so I write an Adapter that provides the same behavior with a different data format.
If we do this kind of thing enough, then eventually we bump into behavior of Theirs that we consider annoying or repetitive and that we hide behind a more-interesting Adapter that is probably more involved than a Pass-Through.
"The line" is somewhere in between these two things.
What if we build an Adapter that catches exceptions and turns them into Either.left values? What if we publish 3 independent interfaces that break apart an annoyingly-large interface of Theirs? What if we publish a single interface that unifies an annoyingly-fragmented collection of interfaces of Theirs? Those all sound quite Pass-Throughish to me.
What if we take a collection of custom-built parsers and hid them behind a unifying interface so that we could compose them in the style of Parser Combinators? Is that Pass-Throughish? I'm not entirely sure.
Those are just some ideas that sprang to mind. I hope something helped.
Those are the examples I was missing. Thank you!
Without having seen the content being referenced in the ask, I'm intrigued by the varying interpretations in the comments.
Discussions of facades, adapters, dependency abstractions, and Git trunking models all used as examples for "pass-through interface". 🤔
Evidence of both how it is critical to be clear with definitions, and how software engineering basically never has a single correct solution.
From the art of unix programming, bc was a normal calculator, dc was an RPN.
They implemented bc by simply calling dc.
http://www.catb.org/esr/writings/taoup/html/ch07s02.html
> While the modern GNU implementation of bc(1) is standalone, the classic version passed commands to dc(1) over a pipe. In this division of labor, bc(1) does variable substitution and function expansion and translates infix notation into reverse-Polish — but doesn't actually do calculation itself, instead passing RPN translations of input expressions to dc(1) for evaluation.
At least to me, a "pass-through" interface would wrap some other interface, but present an identical one. I'd consider your example to be a fairly advanced form of one object implementing it's behaviour in terms of another. But as others have said, it's fascinating to see the many interpretations people have of the original question.
If it's exactly the same interface, maybe v0 should call the original interface, but if future versions also do, then I'm struggling to see why implement a 2nd interface at all?
An example would be to add logging, or statistics, or perrformance measurement, and so on. The apparent functionality for the callers doesn't change at all, but extra side-effects have been added. This sort of pass-though could also massage arguments and/or return values, I suppose.
I asked Chat GPT and here is what I got: A "pass-through interface" in programming or system design refers to a type of interface that transparently passes data or commands from one component to another without modifying them. This term is often used in contexts where data needs to be routed or transferred between different layers or parts of a system, but the intermediary doesn't need to (or shouldn't) alter the data in any way.
For example, consider a computer system where you have an application that needs to access a database. If the application communicates with the database through a pass-through interface, it means that the commands it sends (like SQL queries) are passed directly to the database without any alteration, and the responses from the database (like query results) are returned back to the application as is.
This kind of interface is useful in situations where the integrity of data or commands is paramount, and where the intermediary's role is simply to facilitate communication rather than process or analyze the data. It's a way of ensuring that the data remains consistent and unchanged as it moves through different parts of a system.
Applying the concept of a "pass-through interface" to everyday life and work situations involves adopting a mindset of transparency and direct communication, where information is relayed accurately and without unnecessary modification. Here are some examples of how this can be applied:
Clear Communication in Workplaces: In a professional setting, acting as a pass-through interface means conveying messages or instructions from one party to another without adding personal bias or alterations. For instance, if a manager relays a directive from senior leadership to their team, presenting it as it was originally intended ensures clarity and consistency.
Information Sharing: When sharing information with colleagues or friends, try to present facts and data as they are, especially in critical scenarios like medical advice, legal information, or technical instructions. This helps to prevent misunderstandings and misinformation.
Mediation and Conflict Resolution: In situations where you might be mediating a discussion or conflict, acting as a pass-through can be beneficial. By neutrally conveying each party's perspective to the other without adding your interpretation, you can help facilitate a clearer understanding and potentially resolve conflicts more effectively.
Education and Training: As a trainer or educator, conveying information accurately and in its original context is vital. While interpretation and elaboration are part of teaching, ensuring that the core information is passed through correctly is crucial for accurate learning.
Project Management: When managing projects, acting as a pass-through for client feedback or upper management directives ensures that the team receives unaltered and clear instructions, leading to better alignment with the project's goals.
Personal Relationships: In personal relationships, being a good listener often means absorbing what the other person is saying without immediately interpreting or judging. This can be seen as a form of pass-through communication, where you allow the person to express themselves fully before responding.
In essence, using a pass-through interface approach in life and work is about ensuring that communication remains as unfiltered and undistorted as possible, which is crucial for maintaining transparency, trust, and clarity in various scenarios.
This doesn't help on the example front, but it's possible I'd describe such interface as a proxy, or maybe facade or adapter, although I think that would be stretching the usual implications of the last two. Even for "proxy" I'd be most likely to use it if there was an element of forwarding api requests elsewhere (another thread, process, node, etc). Also, perhaps "wrapper"?
One possible example that popped into my head: sometimes it's useful to track the operations performed via an interface (possibly logging, possibly recording statistics, etc). This might be a good motivating example for the idea.
That example could be a decorator, too. So many options!
A "decorator" is another good term, I think.
I used it already a couple of days after I read the chapter when I had to change a small java application I wrote 3 years ago. The app watches a directory for new files, does some transformations and then writes into kafka.
We are currently migrating to kubernetes and the java WatchService did not work inside a container. Therefore I created a new "FileDetector" interface and a "WatchingDetector" implementation by mostly copying the old code. Tests were still passing.
I could then easily write an alternate "PollingDetector" Implementation and replace the old one while tests were still passing.
https://trunkbaseddevelopment.com/branch-by-abstraction/#software-example
I think this is reasonably common use a "pass-through" interface, especially initially to introduce the abstraction layer. I might expect that abstraction to move away, at least a bit, from a straight pass-through model as the new version is implemented, since that new version might bring along some new features / behaviour.
https://www.branchbyabstraction.com
A bit unrelated examples that could fall in there.
Linux WINE implementing windows API over Linux API to run windows applications on linux without emulation. Or also, any wrapper around basic C/posix library API to provide easier to use API.
In these case, you are never going to get rid of the old one, but your application can use the new one everywhere and be easier to understand.
Also thinking about the python random API, that provides different high level functions just implemented over the low level random -> [0, 1[ to simplify calling code. `randrange`, `choice`, `choices` that are more expressive than seeing the actual arithmetics (+ with low level implementation possibles)
So any "helper" function to use your API could fall as example. "pathlib.write_text" instead of having to do "with open(path, mode=whatmodeisrequiredagain?) as fd: fd.write()". The code executed is actually the same, but the reader understands better what is important. And programming is anyway just doing passthrough over basic CPU operations.
Going back to the context.
I imagine that in the case of that chapter, it was more, design the API you would like to have to use in your application. Write it using your old legacy API. Then you can in parallel migrate your users to the new API to improve their code, and implement the new interface without relying on the old interface. It would anyway be based on another interface, kernel/library/low level something, and so providing a "passthrough" again, but not anymore your own corporate API.
My mind lept less to code structures and patterns and more to what ChatGPT referenced with its SQL example. In many cases there is a need to support a "newer" type of interface such as REST or SOAP. Organizations are littered with systems that wrap legacy interfaces in order to present them to new clients using a different protocols.
A pass-through interface in the context of programming languages refers to an interface that simply delegates method calls to another object. It acts as a transparent layer, forwarding calls to the underlying implementation without adding any additional functionality. This pattern is often used to provide a consistent interface while allowing flexibility in the actual implementation.
Here's a simple example in Java:
```java
// PassThroughInterface.java
public interface PassThroughInterface {
void performAction();
}
// ImplementationA.java
public class ImplementationA implements PassThroughInterface {
@Override
public void performAction() {
System.out.println("ImplementationA is performing the action.");
}
}
// ImplementationB.java
public class ImplementationB implements PassThroughInterface {
@Override
public void performAction() {
System.out.println("ImplementationB is performing the action.");
}
}
// PassThroughProxy.java
public class PassThroughProxy implements PassThroughInterface {
private PassThroughInterface implementation;
public PassThroughProxy(PassThroughInterface implementation) {
this.implementation = implementation;
}
@Override
public void performAction() {
// Additional logic can be added here if needed
implementation.performAction();
}
}
// Main.java
public class Main {
public static void main(String[] args) {
PassThroughInterface proxyA = new PassThroughProxy(new ImplementationA());
PassThroughInterface proxyB = new PassThroughProxy(new ImplementationB());
proxyA.performAction(); // Output: ImplementationA is performing the action.
proxyB.performAction(); // Output: ImplementationB is performing the action.
}
}
```
In this example, `PassThroughInterface` defines a method `performAction()`. `ImplementationA` and `ImplementationB` provide concrete implementations of this interface. The `PassThroughProxy` class acts as the pass-through interface, taking an instance of `PassThroughInterface` during its construction and delegating the `performAction()` call to the underlying implementation. The `Main` class demonstrates how you can use the pass-through proxy with different implementations.
[Pass-Through Interface Example](https://chat.openai.com/share/79bf136b-8ac3-409b-917c-eda6b62c93be)
In my mind, pass-through is a general thought approach, while patterns like adapter and facade are recipes for implementing the pass-through concept in different situations.
My example for a pass through:
We had apis used by our customers. We were committed to supporting old versions of each api for several years. When a new version of an API is published, a pass through is created to expose the old version, and convert it on the back end to the new version, providing appropriate defaults, etc.
I usually use a façade to simplify the interface when a class has many dependencies that are similar.
Example:
class depends on User interface, Post interface, Comment interface.
Post.Get
Post.Create
Post.Update
User.Get
User.Create
User.Update
...
My class only consumes:
User.Get
Post.Get
Comments.Get
I want a façade that has:
GetUser
GetPost
GetComments
So I can only deal with one dependency rather than three.
So finally reading the chapter in question (which is to say, page 11)... It's clear that a "pass-through interface" as used in the text, does not mean *just* an interface that preserves exactly the interface of the wrapped objects which was my interpretation of the fragment starting off this post.
So I think the concepts like "facade" and "adapter" and "decorator" would all be possible synonyms for the concept being discussed. To me, the first two especially have a connotation of modifying or extending the wrapped object's interface somehow. A "decorator" can certainly do the same, I think. Again, just for me personally, "proxy" is too strongly connected in my brain with forwarding requests over some sort of communication link.
To me personally though, the phrase "pass-through" is a synonym for "transparent" which implies a drop-in replacement that may add some desired side effects, eg, logging, perf measurements, maybe caching, etc, but which requires no or negligible change to the existing clients of the wrapped object.