3

I thought I understood what abstraction meant - refactoring code so that it applies to more general use cases. However I have recently learned that some types of abstraction may actually be indirection, and am questioning my understanding.

I'd like to see some a specific example (of code in Javascript - not a description) which illustrates the difference between the two concepts.

I'm specifying Javascript because I only really know JS well (Though Python is similar enough that I could probably follow along.)

  • Have a look at softwareengineering.stackexchange.com/questions/111756/…. It deals with the two terms generally, rather than specifically related to JavaScript, so isn't a duplicate, but it helps explain why your question is difficult to answer. There are a bunch of good answers there. And they differ significantly. This is because there is no strict definition of the terms. There is no right and wrong on this issue. – David Arno Oct 4 '19 at 7:49
  • Indirection is one way to achieve abstraction, e.g. by creating a wrapper over some lower-level logic. – JacquesB Oct 4 '19 at 9:42
1

Strictly speaking, indirection is the capability to indirectly refer to something - via a name, or an address, or an index, or whatever (what's direct and what's indirect may be context-dependent). Some forms of indirection are built into the language; e.g., when you pass an object to a JavaScript function, and call a method on it, you are not actually calling the method directly, because that object could be anything, so there are several candidate implementations (and the method could even be inherited, so it may not be "on" the object at all). You are actually specifying a name (indirection), and the language provides a mechanism to examine the object and find the function (see this).

You can also use indirection "manually"; e.g., via an array index, or a dictionary key, etc.

The statement "some types of abstraction may actually be indirection" doesn't necessarily make sense under scrutiny. There are different kinds of abstraction, but in very general terms, an abstraction is a thing that you place between two interacting components, and make both of those components depend on that abstraction, so that they can vary independently. (Usually, you'll view one of those components as a client of the other, and therefore say that the object you introduced is an abstraction of that other component; but really, the abstraction captures the essential features of the interaction between them). In OOP, this is most often an interface (captures the "I need these behaviors" - "I provide these behaviors" relationship), but it doesn't have to be. In JavaScript, it can be an implicit thing that only exists in documentation (e.g., a parameter to some method must be an object that implements some set of properties and functions - that's an interface requirement, and that very requirement is the abstraction here, even though it's not explicitly expressed in code). The fact that an abstraction can itself provide means for indirection doesn't make it "not an abstraction".

  • Could we say that abstraction is the abstract concept and indirection a mean to indirectly implement it ? – Christophe Oct 4 '19 at 7:43
  • You could say that. In practice, it's not too much of a stretch to conflate the two. The difference is more of a pedantic one - to reuse David Arno's AB&C labels: "abstraction" emphasizes that there is a more general representation C of some more concrete concept, "indirection" emphasizes that there's some of a label C that refers to something else (via some mapping mechanism - memory address maps to a memory location, index maps to an array element, etc.). BTW: It would help if you shared where you read about this distinction, maybe they meant something specific. – Filip Milovanović Oct 4 '19 at 11:10
  • @Christophe: Sorry about the last bit, confused you with the OP. – Filip Milovanović Oct 4 '19 at 12:32
1

In theory, the difference between the terms is easy to define:

An abstraction exists between two things (A and B) if there is a third thing (C) between them. A and B are abstracted from each other by C.

An indirection (or dereference) occurs if a thing, A is not directly referenced by another thing, B but is instead indirectly referenced through thing C.

Notice any similarity between them? When expressed in such loose terms, they are synonymous. So it's easy to define the difference: there is none. But folk like to have them mean different things and that's where the problems arise.

Indirection is a term that's been used since the dawn of CPU instruction sets (or at least since the Z80 CPU in the early 80's, which is where I first came across it) to refer to using registers to specify an address, rather than the absolute address itself. The term is also commonly used for offsets, such as an index into an array, where the address of the start of the array plus the index, give the absolute address of the element.

But both of these are abstractions too though. We are abstracting the address, rather than directly accessing it. But it's unusual to find people referring to these as abstractions.

Abstraction is a term commonly used in OOP to refer to interfaces and abstract types. Here we are putting an abstraction layer (in the form of an abstract type) between the caller and callee. But that also means we are indirectly referencing the callee; we have an indirection. Again, it's unusual to find people referring to this as indirection though (except in the case of inheritance, where that abstraction is often called indirection).

So in summary, abstraction and indirection are fundamentally the same thing. Folk like to use the terms as if they were different, but because they aren't really different, you'll come across many conflicting definitions of the difference.

So you have a choice: view them as the same thing, or pick a definition of the terms that seeks to differentiate them and stick with that. Either way, just be aware that other folk may have picked a different meaning of the terms to you and so confusion may occur when you try to discuss the terms.

One final point, as you refer to JavaScript. If within the JavaScript community, there exists a consensus on strict definitions of these terms, then when using JavaScript stick to those. I don't believe there is such a consensus, but that may be due to ignorance on my part. The fact you ask this question lends weight to there being a lack of consensus though.

  • Isnt’t there an abstraction when you realize that B is a special case of a more general A, without necessarily having a third thing ? I would not agree that indirection is the same thing. There are uses of indirection that are not related to abstraction. Indirection is only a mean to implement abstraction. – Christophe Oct 4 '19 at 11:20
  • @Christophe, yes in general, but we are talking about the abstraction as it occurs in programming, and in that context there is always (or there's an expectation of) some third thing that calls, or derives from, or otherwise relies on that abstraction (the third thing may or may not be written by you). There's a slight confusion because the word "abstraction" is used in two different ways: (1) the process of generalization, and (2) the end-result of that process, the in-between-thing C that models the abstract idea. BTW, I replied to your comment on my own answer, but forgot to tag you. – Filip Milovanović Oct 4 '19 at 12:30
0

There is no built-in support for traditional abstraction in JavaScript. At least, there are not any types like interfaces and abstract classes. Developers in general don't care for abstraction. Although ignoring abstraction does not cause any problems while writing small piece of code, it can be relatively difficult when you are dealing with a large scale application. On the other hand, abstraction provides ways of dealing with cross-cutting concerns and enables us to avoid tightly coupled code.

For abstraction, one can use interfaces and abstract classes, but JavaScript does not have such features. We can alternatively define interfaces using prototypes. Let’s see how we define an interface in JavaScript.

//ItemDemo interface
var ItemDemo = {
   addItem : function(item){},
   removeItem : function(id){},
   getItem: function(id){}
};

We will now define two implementations for ItemDemo : the first one is to save and retrieve data by ajax, and the second one is to save and retrieve data from cookie.

var ItemDemoAjax = function(url){
   this.url = url;
};
var ItemDemoCookie = function(){};
//Extend the ItemDemofor Ajax
ItemDemoAjax.prototype = Object.create(ItemDemo);
//Extend the ItemDemofor Cookie
 ItemDemoCookie.prototype = Object.create(ItemDemo);
ItemDemoAjax.prototype.addItem = function(item){
   //actual add item code
};
ItemDemoCookie.prototype.addItem = function(item){
   //actual add item code
}

we have defined our implementations for ItemRepo, Let’s create a new class that will make use of ItemRepo.

    var ItemController = function(itemDemo){
   this.itemDemo= itemDemo;
};
ItemController.prototype.add = function(item){
   itemDemo.addItem(item);
};

Let’s make use of one of the ItemController and ItemDemo.

 var itemDemo = new ItemDemoAjax("url");
var itemController = new ItemController(itemDemo);
itemController.add({item:"myItem"});

we have implemented our controller class, we may want to log or profile methods for this class.

var Logger = {
   log : function(log){}
}
var ConsoleLogger = function() {};
ConsoleLogger.prototype = Object.create(Logger);
ConsoleLogger.prototype.log = function(log) {
   //actual logging code
}
// Define new Controller
var LoggingItemController = function(itemDemo , logger){
   this.itemDemo = itemDemo ;
   this.logger = logger;
}
LoggingItemController.prototype = Object.create(ItemController);
LoggingItemController.prototype.add = function(item) {
   this.logger.log("start");
   this.itemDemo .addItem(item);
   this.logger.log("stop");
};
var consoleLogger = new ConsoleLogger();
var itemDemo = new ItemDemoCookie();
var loggingItemController = 
   new LoggingItemController(itemDemo , consoleLogger);
loggingItemController.add("item");

we can apply classic patterns easily by using interface definitions

var ItemDemoFactory = {
   getItemDemo : function(type) {}
};
var ConcreteItemDemoFactory = function() {};
ConcreteItemDemoFactory.prototype = Object.create(ItemDemoFactory);
ConcreteItemDemoFactory.prototype.getItemDemo= function(type) {
   if (type === "ajax") {
      return new ItemDemoAjax("url");
   }
   return new ItemDemoCookie();
};

Using interfaces in JavaScript not only increases maintainability, it also increases testability:

var ItemDemoMock = function(mockItem){
   this.mockItem = mockItem;
};
//Extend the ItemDemo for Ajax
ItemDemoMock .prototype = Object.create(ItemDemo );
ItemDemoMock .prototype.getItem = function(id){
   return this.mockItem;
}
function testGetItem(){
   var mockItem =  "mock";
   var mockItemDemo  = new ItemDemo Mock(mockItem);
   var itemController = new ItemController(mockItemDemo );
   //An equal method is needed.
   equal(itemController.getItem("id"), mockItem);
}

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