Rust is a language of this generation where we need be highly productive as a developer at the same time writing memory efficient, world class softwares
Rust is a multi-paradigm programming language focused on performance and safety, especially safe concurrency. Rust is syntactically similar to C++ but provides memory safety without using garbage collection. — Wiki
In previous part Design Dig - Rust Design Principles, we covered major internal design principles of Rust language. And how it makes Rust memory-safe and designed without Garbage collector. Rust is majorly known for
Performance: Rust is exceedingly fast memory efficient without any garbage collection. Hence running from embedded devices to power-packed systems
Reliable: Rust has very rich type system and ownership model. It guarantees memory and thread safety, having one less issues to worry about
Productivity: As discussed Rust is memory safe, hence have robust compiler making it error-free. And it has cherry-on-top with rich tooling (built-in auto formatter) and package manager integrated within Rust itself.
Here we will discuss about common design principle that Rust has:
Table of Content
- LoD - Low of Demeter
- Encapsulation - Keep it hidden
- Principle of Least Surprise
- Single-Choice
- KISS - Keep It Short and Simple
- SOLID
- DRY - Don’t Repeat Yourself
- DbC - Design by Contract
- CQS - Command-Query-Separation
- Uniform-Access
- Persistence-Closures
- Self-Documentation
Law of Demeter (LoD)
The Law of Demeter (LoD) or principle of least knowledge is a design guideline for developing modern software(s). Think of LoD as loosely coupled component/unit of software that has least knowledge of or dependency on component/units outside of it.
A given component/unit should hide its information while assuming as little as possible about any other unit(s)/component(s).
For example, if unit A; instantiate unit B, and B instantiating unit C. Then unit C should not assume or have anything to do with unit A as no direct link is established. While unit A can communicate to unit B, and unit B can communicate to unit C.
Encapsulation
Encapsulation is most common design principle being followed while implementing software(s). This designs principles concerns bundling data with methods that operates on that data in one unit, restricting direct outside access to it.
Assume analogy of a restaurant, where you driven into its area open for public, order and drive-out. You have no idea how your order was packed, what process was followed.
Principle of Least Surprise (PoLS)
Principles of Least surprise applies to software design, wherein a component or unit of a system should behave in a manner as user expects them to without throwing any surprises.
If we take analogy of a restaurant; a customer ordering a type of dish from menu should get delivered same as shown in menu pictures. There should not be any surprises
Single-Choice
“Whenever a software system must support a set of alternatives, one and only one module in the system should know their exhaustive list.”
- Bertrand Meyer: Object-Oriented Software Construction
As per Single-Choice design principle, A list of choices should be always confined one module/unit/component. If these choices are to updated at later stage, these are changed at just one place. The point of Single Choice or it can be called Single source of truth.
The number of modules that know the list of choices should be exactly one and only one.
KISS - Keep It Short and Simple
KISS (Keep It Short and Simple) or (Keep It Simple, Stupid) principle states that most systems work best if they are kept simple rather than made complicated. Therefore a unit should be broken until it can become simple and short; making it easy to understand.
As per Albert Einstein - If you can not explain it well, you do not understand it well enough. So while designing a Software always design simple and short units.
SOLID
The SOLID principle is five principles of System Design clubbed together. These are a set of rules or best practices to follow while designing a class structure. Let’s discuss them one by one:
- Single Responsibility Principle (SRP)
A unit should only have a single responsibility, that is, only changes to one part of the software’s specification should be able to affect the specification of the class.
The Single Responsibility Principle states that a unit should do only one thing. Therefore, it should have only a single reason to change. In technical terms, only one change in software’s specification be able to affect the specification of that unit.
If we take analogy of mailman, its single responsibility is to deliver mails in its jurisdiction. Any changes in guidelines/process to deliver mail should affect mailman
- Open/Closed Principle (OCP)
In system Design, the open–closed principle (OCP) states “software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification”; that is, such an entity can allow its behaviour to be extended without modifying its source code.
Modification here, means changing the code of an existing class, and extension means adding new functionality. So when we extend functionality we are just adding additional behaviour without changing exiting behaviour. While modification means we are changing existing behaviour hence increasing risk of potential bugs.
Let’s take an analogy of Coffee Machine, you can find many different coffee machines. Machine manufacturer has implemented a simple coffee machine that brews coffee, however, manufacturer in future can extend functionality of coffee machine by adding powder mixer, thus only extending existing functionality not modifying existing ones
- Liskov Substitution Principle (LSP)
The Liskov Substitution Principle states that subclasses should be substitutable for their base classes. For class B that is subclass of class A; we should be able to pass an object/representation of class B to any method that expects object of class A without any surprises. This is what expected behaviour of inheritance is that it implements all methods of superclass while implementing its own methods.
- Interface Segregation Principle (ISP)
Interface Segregation Principle, as name suggest is about separating interfaces. That means having to develop many client-specific interfaces is better that having one general purpose interface.
- Dependency Inversion Principle (DIP)
The Dependency Inversion principle states that our classes should depend upon interfaces or abstract classes rather than on concrete classes and functions.
DRY (Don’t Repeat Yourself)
DRY (Don’t Repeat Yourself) is similar to Single-Choice Principle
Every piece of Information should have single source of truth which must be single, unambiguous, authoritative representation within system.
If information have multiple source, it would add ambiguity to it and hence can be misleading. Similarly in system design, if a unit which discharge a single function should be available at one place, copying it over different module; when changed would lead to bugs. As it is never guaranteed that it would be changed every place it is implemented. This would also burn productivity of resources and thus counterproductive
Design by contract (DbC)
Design by Contract is driven by assertion and the assumption that an assertion will always be true. It can only be false due to Contract not being fulfilled hence buggy.
An underlying assumption is that components interact with one another in a client-server
model. A server
makes certain promises
(also called obligations
) to provide benefits to clients
, and client
components may assume these promises
will be kept. The contract also specifies pre-conditions
, postconditions
and invariants
, which participating components must honor.
If we take analogy of postal service, the precondition would be that we provide correct delivery address and return address (in case it remain undelivered). The postconditions here is that if delivery is made we are notified of same, if not then parcel is returned back to us.
Command-Query-Separation(CQS)
Command-Query-Separation was coined by Bertrand Meyer in his book “Object Oriented Software Construction”. The prominent idea is that we should divide an object’s methods into two sharply separated categories:
Queries: Function/methods returns a result and do not change the observable state of the underlying system (are free of side-effects).
Commands: Commands can change the state of underlying system but do not return a value and are permitted to produce side-effects.
In a mail man analogy, the parcel and its content and queries and command is the delivery mechanism. While delivery may produce side-effects (i.e remained undelivered) but content of parcel will remain same
Uniform-Access
“All services offered by a module should be available through a uniform notation, which does not betray whether they are implemented through storage or through computation.” - Bertrand Meyer: Object-Oriented Software Construction
This simply means that the notation to refer or access a feature of a unit should not differ depends on whether it is an attribute (data) or behaviour (method)
Persistence-Closure
“Whenever a storage mechanism stores an object, it must store with it the dependents of that object. Whenever a retrieval mechanism retrieves a previously stored object, it must also retrieve any dependent of that object that has not yet been retrieved.” - Bertrand Meyer: Object-Oriented Software Construction
In simple terms it means, if whatever storage mechanism used, a unit when stored; should also store its dependency as well. So when that unit is restored its dependency also restored with it and available for execution.
If we take analogy fo mailman; when a parcel is tracked it is known that who posted it, which circuit it came by and what is final delivery destination is. While parcel itself has receiver and sender address, PIN tells us which nearest Post office it belongs to and what path it may have taken.
Self-Documentation
There should not be any need to additional documentation of a module. The designer of a module should strive to make all information needed about module as part of module itself. This makes documentation more profound as changes to documentation with the module will also changes documentation with it without searching elsewhere. And thus keeping documentation up-to-date.
The designer of module should also strive to keep the code self documentary with behaviour and attributes being similar to what they are intended to do.
About The Author
I am Pankaj Baagwan, a System Design Architect. A Computer Scientist by heart, process enthusiast, and open source author/contributor/writer. Advocates Karma. Love working with cutting edge, fascinating, open source technologies.
To consult Pankaj Bagwan on System Design, Cyber Security and Application Development, SEO and SMO, please reach out at me[at]bagwanpankaj[dot]com
For promotion/advertisement of your services and products on this blog, please reach out at me[at]bagwanpankaj[dot]com
Stay tuned <3. Signing off for RAAM