Chain of Responsibility: At runtime, command objects are selected to execute responsibilities. One common example is FilterChain used with servlets where multiple filter commands are executed on receiving a Request.
The chain-of-responsibility pattern is structurally nearly identical to the decorator pattern, the difference being that for the decorator, all classes handle the request, while for the chain of responsibility, exactly one of the classes in the chain handles the request. This is a strict definition of the Responsibility concept in the GoF book. However, many implementations (such as loggers below, or UI event handling, or servlet filters in Java, etc.) allow several elements in the chain to take responsibility.
Mediator: Two objects are decoupled as they do not communicate to each other directly, but via a mediator.
Unlike adapter where the communication is one way and caller is aware of provider, in mediator a middle layer helps both parties to communicate.
Visitor: Implemented using accept() and visit() methods, the component being visited needs to implement accept method and visiting component implements visit method.
State: An object can manage its behavior based on state. For example, a mobilealert object can have state as ring, silent, vibrate and based on state its bevior is changing. Here is a good example implementation https://www.geeksforgeeks.org/state-design-pattern/
After discussing creational design patterns in the last post, let’s go over structural design patterns.
Adapter: One of the most common structural patterns that helps two incompatible components talk to each other. For example, for a caller that communicates in JSON, but the provider service only understands XML, an adapter helps in this situation.
Composite: To manage the cases where there can be a cyclic relationship between objects, for example, a Manager himself is an Employee, and at the same time Manager has a relationship (manages) other employees.
Facade: Hide complex implementation details from the caller, which calls the facade. The facade layer then communicates with various other implementing components and returns the final response back.
Proxy: A common pattern hiding the actual implementation from the caller client. The client calls the proxy class or proxy layer, which in turn communicates with implementing component.
Bridge: The pattern that helps decouple an abstraction from its implementation.
Flyweight: The flyweight pattern helps reuse objects where ever possible and only creates a new object if the object is not available.
It sounds a bit overlapping with the object pool pattern, but there are significant differences. An object pool is a creational pattern whereas flyweight is a structural pattern. Object pool makes sure objects of the same type are pooled, whereas flyweight takes care of different object implementations, stored as a singleton.
Singleton pattern: Most commonly used creational pattern where we want to make sure a single object is maintained for a class.
Factory Pattern: Representing a real-world scenario, one can let the factory create objects of multiple types inheriting similar features, for example, a car factory building sedans as well as hatchbacks.
Abstract Factory Pattern: Additional layer above the factory pattern, for example, a vehicle factory has two sections a car factory and a bike factory, and the car factory further produces multiple types of cars.
Prototype Pattern: Object prototypes are created in advance, which is clones on demand and a new object is returned.
Builder Pattern: A commonly used pattern (DocumentBuilder and StringBuilder in Java), that breaks object creation activity into multiple steps.
Object Pool Pattern: The creation of an object can be an expensive affair for complex objects, a very common example is database connections. In such cases, the object pool pattern helps in reusing the objects once created, hence improving the performance of the system.
The object pool pattern is a software creational design pattern that uses a set of initialized objects kept ready to use – a “pool” – rather than allocating and destroying them on demand. A client of the pool will request an object from the pool and perform operations on the returned object. When the client has finished, it returns the object to the pool rather than destroying it.
In the series of exploring designs for popular systems, I will try to come up with Twitter’s system design today.
User should be able to Tweet
User should be able to follow and view tweets of others on their timeline
User should be able to search for tweets
User should be able to view current trends
Non Functional requirements
Scale 150 million users with 500 million tweets per day or ~5500 Tweets/Second
30000 view of Architecture
There are one or two aspects of the above design which are very interesting. The first one we can see is the user timeline. This can be a complicated piece, whenever a user login into the app, he should see his timeline, which will show all the tweets from people he is following. The user might be following hundreds of accounts, it will not be feasible to calculate tweets from all these accounts at runtime and create timeline data. So a passive approach makes sense here, where we can keep the user timeline data in a cache beforehand.
Say user A is following user B, and user B publishes a new tweet, at that time itself, user A timeline will be updated with a new timeline getting added to user A timeline data. Similarly, if 100 users are following user B, all the timelines get updated (Fanout the tweet and update all timelines).
It can get tricky if user B has millions of followers. A different approach can be used in this case. Assuming there are not many such popular users, we can create a separate bucket for handling these popular users. Say user A is following user C (celebrity), so instead of updating the timeline for C beforehand, tweets for all such celebrity users can be pulled in real-time.
Another important aspect is hashtagging and trends exploration. For all the tweets coming in, the text can be tokenized and tokens can be analyzed for most usage. For example, when a cricket match is going on in India, many people might tweet with the term match or cricket. Again these trends might be geo-location-based as this particular trend is a country-specific one.
Modularization introduced in Java 9 is to help developers call out explicitly what are they going to use in their application and what features are they ready to expose.
You can see that JDK itself now is viewed as a combination of modules rather than a single monolith unit. One can mention all modules they are going to use in the application and keep the application lightweight. This can be done by adding a “requires” section in the module-info file.
Another problem Modules solve is when you are sharing your library (a jar), it exposes all the packages, though you might not want users to play around with internal helper files. The “exports” keyword gives you control over what is being exposed from the package.