Skip to content

Latest commit

 

History

History
90 lines (71 loc) · 5.64 KB

README.md

File metadata and controls

90 lines (71 loc) · 5.64 KB

Fast food [Dependency injection]

Description

Module Fast food is an example of the Dependency injection pattern. This is for sure one of the most common patterns used in OOP programming, because it helps us to reach a high level of decoupling.

This one is about a food selling business. They can take orders from different channels and, as you can guess, each channel has its own particularities. Eg, if you order from a telematic channel, it is easy to understand that they will always need al least your location for delivering. And, additionally, each one of these will need its own specifications, like an e-mail for web or a phone number for telephone clients. But, if you'd rather to pass by and pick up your food, they still have the takeaway traditional channel, in which they will only requiere a name to shout when your meal is ready.

So, as you can see, channels have some functions in common, but they also have differences. Everything could be easily solved with inheritance relationships for now. But, how cool would it be to have promos? The owners approve the idea, but they are interested in promoting some channels, not all of them. And... those promoted are the takeaway and the web, not in the same inheritance hierarchy line. No problem! We can declare Promoted as an interface in which every promoted channel will be able to check if its offer is active and to tell the promo:

interface Promoted {

    boolean isPromoActive();

    String announcePromo();
}

The most interesting details of these example come next. If we are willing to be good developers –and we are, for sure–, we have to be organized and don't mess our code. If you think about this scenario in real life, people who deliver the orders don't have to know about the details of the promos or the available channels, as well as people who manage one channel doesn't have to know about the others. In every business, the tasks are divided and isolated as much as possible, so that an internal change in any area/department should not affect the other ones.

Back to our code, you can see we have an Order class. Its responsibility is just to process the orders and keep a counter updated. Of course, this class has a dependency with a Channel, but whichever the concrete implementation is, it is capable of running the order, just basing the steps on the interface methods, which are the necessary minimums for a Channel to implement for being operative. Every specific task each channel wants to add would be internal and could be put within the implementation of the common methods. The best part is that the Order class doesn't know how many channels there are, the specific methods they use to operate, or even their names!

We can find a similar behaviour about Promoted channels. We know they exist, but we don't know and don't care which ones are or what they do when they have a Promo. When the time comes for us to process the order, we just call the interface methods that let those channels «do their thing», and then we come back to the common procedure for every channel:

public class Order {

    // Other constants, attributes and methods.

    public String process() {
        this.updateOrdersCounter();
        return this.channel.welcome()
                .concat(this.channel instanceof Promoted promo && promo.isActive() ? promo.announcePromo() : StringUtils.EMPTY) // Checking promos
                .concat(this.getOrderNumber())
                .concat(String.format("%n[...]%n%s", this.channel.serveClient()))
                .concat(this.channel.farewell())
                .concat(SystemUtils.LINE_SEPARATOR);
    }
}

Notice that this code complies very well with the Open/Closed principle. If we want to add any new channel, promoted or not, or modify the existing ones about their particular required data or their promos, we don't need to change the already existing code in Order or Channel classes, least of all their implementations. We could've leaned on inheritance hierarchy relations for the channels, but remember that this is only recommended when the inheritors share common attributes. In this case, we put that in practice with the TelematicChannel for simplifying the location attribute management, but always remember Java doesn't support multiple inheritance, so it's usually a better choice to use an interface whenever it's possible.

Wrapping up: the fact that an Order always receives a reference to the channel in the constructor –injected dependency– makes it possible to standardize the logic of processing every order whichever channel it came from. This is the key of this Dependency injection pattern and a sign of well planned structured and low coupled code.

Subjects, technologies and contents

  • Single Responsibility Principle (S from SOLID).
  • Open/Closed principle (O from SOLID).
  • Use of icu4j library to generate ordinals in text may be of interest.