DEV Community

Discussion on: The Bridge Pattern - Design Patterns meet the Frontend

Collapse
 
jwp profile image
John Peters

I feel that all abstraction, as shown in this example are just contracts of what is to be contained by concrete implementations. Whether or not that abstraction includes other interfaces is up to the designer; however, a more overarching principal is that "favoring composition over inheritance" simply means, add anything you need as a property or field/function (typed or non-typed), in this case, it's a Color property type defined by a Color contract/interface.

In the context of Composition, we don't call this a pattern, as it is simply containment of what is needed e.g. Color and Shape (and the rules to follow). Note that the exact same thing can be achieved merely defining classes, no need for interface or abstract classes. In fact, ultimately there is no need for types either.

Most JavaScript folks will not see the value in this example. They don't see value in interfaces and do not use abstract declarations. Shoot they haven't traditionally even used the class construct. For them it's just close-to-the-metal scripting/get-it-done, simple key value pair programming all contained in a JSON Object. In some ways they are right, it's much easier to think that way than in "abstract" OOP terms.

Alas the style we choose for the right job is always paramount, need types? Implement them! Don't need types? Leave them out. Need abstractions? Put them in etc. etc.

Thanks for the post.

Collapse
 
oliverradini profile image
OliverRadini

Maybe I'm missing something, but it seems that the distinction between an abstraction and implementation only becomes relevant when the language has a distinction between the two?

In some senses, Javascript developers work only in abstractions; if I have two different API services in Javascript, and they both implement a .get, how would we distinguish, in Javascript, between writing a function which relies on an implementation of a thing-which-needs-a-get-function, and an abstraction of a thing-which-needs-a-get-function? With an example:

const callGet = apiService => url => apiService.get(url)

const apiA = {
  get: url => console.log(`called get with url ${url} on apiA`)
}

const apiB = {
  get: url => console.log(`called get with url ${url} on apiB`)
}

I can call callGet using either apiA or apiB. Unless I'm missing something here, I don't really see, in fact, how non-OO languages have any way at all of caring about the distinction between abstraction/implementation; I'd be very glad to be pointed in the correct direction though!

Collapse
 
jwp profile image
John Peters • Edited

In traditional Javascript there is no guarantee that the service you show, can know ahead of time if both objects (shown above) have implemented the getter pattern. It would only become known at runtime.

This is my #1 reason for advocating class design with types, and why I prefer Typescript over Javascript.

If we use typed classes, then the question is answered before compile time via intellisense. If either of the objects passed in did not implement getters the red squiggly line would show via intellisense while coding.

Therfore questions like 'how do we know' are answered by Intellisense. It's perhaps the greatest tool for programmers ever; and it works best on Typed objects.

Thread Thread
 
oliverradini profile image
OliverRadini

Indeed, and I'm also a big fan of Typescript; I think the question of catching at runtime vs compile time is a separate question, though.

My main point was that it isn't really possible to assert that Javascript developers don't use abstract declarations. That's all they use.

Collapse
 
coly010 profile image
Colum Ferry

Thank you for the constructive feedback!
I feel like I found this pattern difficult to wrap my head around was because it is very close to the Strategy Pattern, and it really is just down to fulfilling contracts that the developer defines themselves.

Collapse
 
jwp profile image
John Peters

I studied patterns for years. In the end I abandoned them. Reason was that all OOP when refactored as far as possible turns into single responsibility functions anyway. The javascript folks love functional programming, so this aligns there too.

Instead of patterns I now create lots and lots of small SRP functions that are compose-able. Also, I no longer use classical inheritance. I find doing this aligns nicely with web based programming. I still use Typescript because I love type safety and I use Classes for the same reason. Why find run-time errors when you can find them via intellisense before the compile?