top of page

Subscribe to get best practices, tutorials, and many other cool things directly to your email inbox

  • Writer's pictureAhmed Tarek

When Implementations Affect Abstractions

Could the knowledge of implementations affect abstractions design?


Could the knowledge of implementations affect abstractions design? Best Practices Design Patterns Software Architecture Engineering
Photo by UX Indonesia on Unsplash

If you ask a developer if the knowledge of the available implementations could affect his abstractions design, most probably he would say:

No, if this happens then my knowledge of the system is not enough for designing the abstractions.

At some point on my career as a Software Engineer, I would have agreed with this. Actually, I would have provided the same answer if someone asked me the same question.


However, on more than one occasion I came across some solutions which made me change my mind on this.


Long story short, yes, you need to understand your system and the capabilities that should be provided by each module to be able to design your abstractions. However, this is not enough.


Exploring some of the possible implementations could help you enhance your abstractions.


Ok, let’s say that your knowledge of the system you are designing and all its interactions is a part of your job and you actually need it to do your job. However, being creative about building this knowledge is something else.


What we are discussing now is how to build this knowledge. One of the best and creative ways is that once you have an abstraction in mind, you try to explore the possible implementations. This would put your mind in an effective state that would help you link too many loose ends.


 

Could the knowledge of implementations affect abstractions design? Best Practices Design Patterns Software Architecture Engineering
Photo by Afif Kusuma on Unsplash

The XML/SQL Example


Let’s assume you are designing a solution for an application that would handle an inventory or something similar. At some point, you would need to save/retrieve data to/from a datastore.


Now, it is clear that you would implement a module for handling writing and reading data. And for sure you would need to abstract this module into an interface so that the rest of the system modules interact with this abstraction layer rather than the concrete implementation.


At this point, you don’t have any implementation in mind, all what you care about is designing the abstracted layer according to the system needs.


Therefore, you come up with a design like this:

public IEnumerable<Item> GetAllItems();
public void AddItem(Item item);

It is good. Now the other system modules can use this abstraction layer to read and write items.


However, let’s try to imagine what the possible implementations of this abstraction layer could be.


One of the possible implementations is XML storage.


Therefore, when implementing this, we would provide some code to open an XML file, read elements from inside, write elements,… and in case of adding multiple elements, it would be easy to loop on the items and call the AddItem method. Great…


Another possible implementation is an SQL Database.


Therefore, when implementing this, we would provide some code to open connection to the SQL server, read items from tables, write items to table,… and in case of adding multiple items, it would be easy to loop on the items and call the AddItem method. Great…


Stop, it is not that great actually. If you are aware of SQL you would know that every time you call AddItem it would open a connection to the server, execute the command, get results, and close the connection. This process is too expensive in terms of performance.


The better known solution for this is to enable and expose batch processing of items through your abstraction layer. In that case, the design of the abstraction layer would be:

public IEnumerable<Item> GetAllItems();
public void AddItem(Item item);
public void AddItems(IEnumerable<Item> items);

Then, the implementer can decide on how to implement the batch processing method AddItems. For some implementations may be it would be good to loop and call the AddItem method, for others it might be a totally different implementation.


Someone might argue that we should have came up with the latest design in the first place even without knowing about SQL.


May be you are right, but, I think if you dig deep into yourself you might find that you are saying this because you already know about similar situations when batch processing is needed… Keep in mind that you can’t undo knowledge.


 

Could the knowledge of implementations affect abstractions design? Best Practices Design Patterns Software Architecture Engineering
Photo by Adrien Ledoux on Unsplash

The Transportation Example


Let’s assume you are designing a solution for an application that manages different means of transportation like cars, busses,…


You want your system to be robust enough to the extent that a new vehicle or mean of transportation could be added and the rest of the modules could interact with it without any problems or breaking logic.


Therefore, you come up with a design like this:

public void Move(Vehicle vehicle);

Ok, it should be working fine for many vehicles, but what about Trains? You need to keep in mind that Trains movement is restricted by tracks that they can’t move outside.


Therefore, knowing that, you start generalizing the idea of having restricted movements, may be for trains or whatever the vehicle is. So, you update your abstraction layer to be as follows:

public void Move(Vehicle vehicle, IEnumerable<MoveRestriction> moveRestrictions);

This should make your design more efficient and robust.


Still, we might argue that this is a too obvious case and it should have been considered in the first place.


Again, may be you are saying that because you already own the knowledge and you can’t undo knowledge. However, what about a totally new domain to you?


 

Could the knowledge of implementations affect abstractions design? Best Practices Design Patterns Software Architecture Engineering
Photo by Hal Gatewood on Unsplash

Queued or Non-Queued Example


Let’s assume you are designing a solution for an application where there is a stream of data passing through (from and to) your application.


You are already aware of your system and its capabilities. You know how fast your environment can handle and process incoming requests. This is great.


However, your system would act as a middleware which would be hooked in between other systems. Therefore, your system should expect incoming requests and should forward outgoing requests and there is no guarantee that the frequency of these incoming and outgoing requests would always be the same.


Before knowing about this last piece of info, you decided that your design would directly handle the incoming requests, process them, and directly forward the result to the next system. That should be ok.


However, after knowing about this last piece of info, now you need to design your system to be more robust. So, you decided to implement queuing. Therefore, now your system would queue the incoming requests, process them in parallel, queue the results in the same incoming order, forward the results to the next system.


Having this design in place, would make your system more robust even if the frequency of the incoming requests is not that high.


 

Could the knowledge of implementations affect abstractions design? Best Practices Design Patterns Software Architecture Engineering
Photo by Jan Antonin Kolar on Unsplash

Paged or Non-Paged Example


Let’s assume you are designing a solution for an application that is a dashboard presenting some data.


At some point you would build the API to retrieve the data. So, you decided to implement the well known Repository Design Pattern. Great, it should work like charm.


However, you didn’t make the deign robust enough for a huge amount of data. So, now the API users can request to get all the data at once and this is too bad.


This problem in the design could have been discovered too early if you generalized the idea of dealing with data in terms of “We apply throttling”.


This would have guided you to apply paging and internal throttling in the first place.


Note: If you are interested into knowing more about this, you can check the article Better Enhanced Repository Pattern Implementation in .NET C#.


 

Could the knowledge of implementations affect abstractions design? Best Practices Design Patterns Software Architecture Engineering
Photo by Elisha Terada on Unsplash

Final Words


Don’t get me wrong, I am not saying that knowledge about the system you are designing is not important or that abstractions should somehow depend on implementations.


What I really mean is that building knowledge about a system is a process. The process itself could be done in different ways but I am sure that being creative would be a great help.


One of the ways I recommend is to explore different alternatives, implementations, nature of interactions, limitations,…


One last note, knowledge is incremental. The knowledge you gain from a system would benefit you on another system. Therefore, keep harnessing knowledge whenever possible as this would help you grow and gain experience.



Recent Posts

See All

Subscribe to get best practices, tutorials, and many other cool things directly to your email inbox

bottom of page
Mastodon Mastodon