0% found this document useful (0 votes)
52 views

ISD Lecture 1

This document discusses an introduction to object-oriented programming (OOP) design guidelines. It covers the importance of OOP principles like abstraction, manageability, reusability, and readability. It also describes key OOP features like encapsulation, inheritance, and polymorphism. Finally, it outlines several software engineering design guidelines for OOP like favoring composition over inheritance and programming to interfaces instead of implementations.

Uploaded by

islam2059
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
52 views

ISD Lecture 1

This document discusses an introduction to object-oriented programming (OOP) design guidelines. It covers the importance of OOP principles like abstraction, manageability, reusability, and readability. It also describes key OOP features like encapsulation, inheritance, and polymorphism. Finally, it outlines several software engineering design guidelines for OOP like favoring composition over inheritance and programming to interfaces instead of implementations.

Uploaded by

islam2059
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 31

Course : CSE325

Information System Design

Lecture Note 1 : Introduction to Design Guidelines

Submitted to:

Md. Shariful Islam Bhuyan


Assistant Professor, CSE, BUET

Submitted By:

Saira Yeasmin - 1805037


Md. Mehedi Imam - 1805039
Md. Asif Shahriar - 1805040
Sanjana Binte Siraj - 1805041
Debojit Pandit - 1805042
Aroma Hoque - 1805058

Subsection: A2 Group: 04
Contents
1 Importance of OOP Principles 2

2 OOP Features 9
2.1 OOP vs Procedural Programming . . . . . . . . . . . . . . . . . . . . . . . . 9
2.2 Features Introduced in OOP . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.2.1 Encapsulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.2.2 Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.2.3 Polymorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

3 Software Engineering Design Guidelines 14


3.1 Favour Composition over Inheritance . . . . . . . . . . . . . . . . . . . . . . 14
3.2 Program to Interfaces, Not Implementations . . . . . . . . . . . . . . . . . . 19
3.2.1 Abstract Class vs Interface . . . . . . . . . . . . . . . . . . . . . . . . 19
3.2.2 Design Guideline: Program to Interfaces . . . . . . . . . . . . . . . . 19

4 Importance of Design Principles 21


4.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4.2 Indicators for good code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

5 DRY 21
5.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
5.2 Rule of 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
5.3 Cases where DRY principle is used . . . . . . . . . . . . . . . . . . . . . . . 22
5.4 Evil Switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
5.5 Resolving Evil Switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

6 KISS and YAGNI 24


6.1 KISS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
6.2 YAGNI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

7 Introduction to SOLID 25
7.1 SOLID means Supple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
7.2 What people confuse SOLID with? . . . . . . . . . . . . . . . . . . . . . . . 26
7.2.1 Not a Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
7.2.2 Not a Framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
7.2.3 Not a Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
7.2.4 Not a Goal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
7.3 SOLID code is Different than typical code . . . . . . . . . . . . . . . . . . . 27
7.4 SOLID helps to avoid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
7.5 The five SOLID Principles . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

1
1 Importance of OOP Principles
/* This section is written by roll 1805037 */

OOP stands for Object-Oriented Programming, which is a programming paradigm that


is based on the concept of objects, which can contain data and code that operate on that
data. In OOP, a program is made up of objects that interact with each other to accomplish
tasks. It mainly works on Class, Object, Polymorphism, Abstraction, Encapsulation and
Inheritance. Its aim is to bind together the data and functions to operate on them. Some
of the well-known object-oriented languages are Objective C, Perl, Java, Python, Modula,
Ada, Simula, C++, Smalltalk and some Common Lisp Object Standard.
The importance of OOP principles are given below:

Abstraction: By using encapsulation and defining classes with well-defined interfaces, OOP
provides a high degree of abstraction. This means that the user of an object does not need
to know how the object works internally; they only need to know how to use it.It focuses
on the essential features of an object and ignoring the details that are not relevant to the
current context.An example of abstraction in OOP could be a car.

Figure 1: Example of Abstraction in OOP

2
A car is an object that has many features such as wheels, engine, transmission, etc.
However, when we use a car, we don’t need to know all the details of how it works. We only
need to know how to drive it - such as how to start the engine, how to accelerate, brake, and
steer.The details of how these methods work will be hidden from the user, allowing them to
focus only on the essential features of the car.

Manageability: OOP supports modularity which allows to break large ,complex program
into smaller ,more manageable components.Each object in an OOP program is responsible
for a specific task, and the objects interact with each other to accomplish larger tasks. Then
we merge and integrate different components and manage the whole process of combination
of different modules,logics and codes.
For example,in building a software application for a car rental company,we create an
object for each component of the car rental process, such as customer information, rental
rates, rental contracts, vehicle inventory, and billing. Each object would be responsible for
its own specific task, and the objects would interact with each other to complete the rental
process.
This approach makes the code more manageable because each object can be developed
and tested independently.If we need to update a specific aspect of the car rental process, you
can focus on that specific object without affecting the rest of the program.

Reusability: OOP allows developers to create reusable code components, such as classes
and objects, that can be used in different parts of the codebase. This helps reduce the amount
of code that needs to be written, leading to faster development and less errors..OOP ensures
reusability by providing mechanisms such as inheritance and polymorphism.A class(subclass)
can inherit the properties and behaviour of superclass class through using inheritance .
This means that the subclass automatically has access to all the fields and methods of the
superclass, and can add its own fields and methods as well. This makes it easier to reuse
code, as we can create a superclass with common functionality and then create subclasses
that add or modify that functionality as needed. In polymorphism , a single method can be
used to operate on multiple different objects. This is achieved through method overriding
and method overloading.

3
Figure 2: Example of Reusable in OOP

In the above code,by using OOP and inheritance, we have created a hierarchy of classes
that allows us to reuse the area() method of the Rectangle class to calculate the area of
both rectangles and squares, and we can also calculate the area of circles using a separate
class. We can use the same classes in different parts of our program or in different programs
altogether, making our code more reusable..

Readability: OOP can ensure readability by providing a clear and organized structure
for code, promoting clear and descriptive naming, separation of concerns, encapsulation,
code reuse, and abstraction. By following these principles, developers can create code that is
easier to read, understand, and maintain over time.Through using encapsulation, the internal
workings of a class are hidden from the rest of the program. This can make code more
readable by allowing developers to treat objects as ”black boxes” and focus on the public
interface of the class, without having to understand every detail of how it works internally.

4
By abstracting away low-level details and providing high-level interfaces, developers can focus
on the overall functionality of a program without getting bogged down in implementation
details.
Suppose we want to simulate a library. We can write all the code in a single class, but this
would quickly become complex and difficult to read. Instead, we can create several classes,
each with its own set of properties and methods like book class(deals with books),patron
class(deals with library patrons) and library class(deals with maintaining books).

5
6
Figure 3: Example of Readability in OOP

Productivity: Productivity means that more output is produced with the same amount
of inputs.The efficiency and and increase in production level is achieved by OOP. .OOP
can improve productivity and efficiency by promoting the creation of reusable, maintain-
able,readable and testable code components that can save time and effort and reducing the
likelihood of bugs and errors during the development process resulting in improved software-
development productivity over traditional procedure-based programming techniques The
overall production cost will be low and can gain huge profit with small effort,less error,efficiently
manageable code.

Maintainability: Maintainable code is code that is organized so that it is easy to find and
fix errors and improve performance.OOP is easily to maintain and helpful for big fixing in
future after product release. Since the design is modular, part of the system can be updated

7
in case of issues without a need to make large-scale changes.Usually the notion of adding
features (extensibility or enhancement) is included in the idea of software maintenance as
well.These help organize code into independent subprograms that helps to fix a specific
subprogram without affecting other part or module in the whole application.OOP provides
a well-defined interface for unit testing, which helps during software maintenance since unit
tests can be rerun after refactoring code to determine if problems have occurred.

Figure 4: Example of Maintainability in OOP

In this example,By encapsulating the properties and methods of an employee within a


single class, we can easily modify or extend the functionality of an employee without affect-
ing other parts of the program. For example, if we wanted to add a new property to the
Employee class, such as department, we could do so without affecting the existing code that
uses the Employee class.Additionally, by providing getter and setter methods for accessing
the properties of an employee, we can control how these properties are accessed and modi-
fied, which can help prevent bugs and maintain the integrity of the code.

8
In conclusion,OOP has become a popular programming paradigm because it helps de-
velopers to write modular, maintainable, and scalable code that can be easily adapted to
changing requirements providing a better way to organize and manage complex software
systems.

2 OOP Features
/* This section is written by roll 1805039 */

2.1 OOP vs Procedural Programming


Early programming languages — such as Fortran and BASIC — all followed the procedu-
ral paradigm, meaning that they were purely imperative in nature. This is because these
languages operated much more in line with the computer’s hardware. And at that level,
programming a computer boils down to telling it what to do at every step in a procedural
manner.

However, when we wish to write more complex code, we find procedural programming
languages to have an array of disadvantages. Coding in a language like C is tedious because
you have to explicitly tell the machine which steps to go through. For the same reason, code
becomes harder to read and is likely to repeat itself. Classes offer a degree of abstraction
that helps the programmer take a more high-level approach and write modular code that’s
cleaner and more readable.

2.2 Features Introduced in OOP


There are three major features in object-oriented programming that makes them different
than non-OOP languages :

• Encapsulation

• Inheritance

• Polymorphism

2.2.1 Encapsulation
Encapsulation, in general, is nothing but a fancy word for packaging or enclosing things of
interest into one entity. Used most commonly in the realms of object-oriented programming,
encapsulation refers to the packaging of data and functions that represent an embodiable
(real world) entity into a programmable enclosure (commonly classes/objects).

The data (variables) here represent the attributes and properties that define the unit,
whereas the functions symbolize its possible behaviors and operations. So, in other words,
encapsulation is the binding of data and methods into this one entity to define itself and the
scope of its operations.

9
Additionally, this bundling of data and methods into individual entities hides complexity
by providing abstracted interfaces (the objects) to program and interact with; instead of
directly with lower-level constituents. More importantly, this protects the object’s internal
state from undesirable external access and modification through a concept popularly known
as information hiding. This proves to be a critical aspect for applications dealing with
sensitive data and also prevents failures in one part of the code to bleed into the rest. So in
brief :

• Encapsulation refers to the bundling of fields and methods inside a single class.

• Prevents outer classes from accessing and changing fields and methods of a class

• helps to achieve data hiding.

Example In Java :

In the above example, we have a private field age. Since it is private, it cannot be accessed
from outside the class.

Making age private allowed us to restrict unauthorized access from outside the class.
This is data hiding.

10
2.2.2 Inheritance
As with the other principles of OOP, inheritance is meant to optimize the work of program-
mers. The role that inheritance plays in this optimization is in allowing software engineers
to create class hierarchies, where classes and objects inherit properties and behaviors from
their parent (or super) class.

A class that inherits from a parent (or super) class is called a subclass or child class, and
objects that receive properties and behaviors from a parent through inheritance are referred
to as child objects. In summary:

• Inheritance provides the way of achieving code re-usability

• We don’t need to write the same code multiple times, again and again

• Rather we can use inherit a version of the given properties of one class into the other
just by extending it.

Example In Java :

11
In this example, we have a base class Teacher and a subclass BiologyTeacher. Since class Bi-
ologyTeacher extends the properties from the base class, we need not declare these properties
and method in the subclass.

2.2.3 Polymorphism
Polymorphism in OOP is inseparable and an essential concept of every object-oriented pro-
gramming language. An object or reference basically can take multiple forms in different
instances. As the word suggests, ‘poly’ means ‘many’ and ‘morph’ points at ‘forms’; thus,
polymorphism as a whole would mean ‘a property of having many forms.’

The object-oriented programming language processes classes and objects by a single in-
terface. It implements the concepts of function overloading, overriding, and virtual functions.
Also, it is typically used for instrumenting inheritance in programming.

Polymorphism is one of the significant OOP concepts. Using Polymorphism, we can have
various or multiple forms of objects, variables, or methods. There can be varied implemen-
tations of the same method as per the class’ need using Polymorphism.

The concept of polymorphism offers great scalability and boosts the code’s readability.
We can define polymorphism to have your unique implementation for the same method within
the Parent class. Generally, it can be obtained using inheritance which implies that the class
must belong to the same hierarchy tree. It denotes an object’s ability to adopt several forms
in various instances. It uses the concept of function overriding, function overloading, and
virtual functions.

Polymorphism is a property using which any message could be delivered to objects of


multiple classes. Each object owns the tendency to respond appropriately based on the class
properties. So we can say:

• Polymorphism allows a specific routine to use variables of different types at different


times

• Presents the same interface for several different underlying data types

• Makes different objects to respond in a unique way to the same message

In this example below a superclass called Animal that has a method called animalSound().
Subclasses of Animals are Cow and Hen. They also have their own implementation of an
animal sound (the cow moooes, and the hen chirps etc.)

12
Example In Java :

13
3 Software Engineering Design Guidelines
/* This section is written by roll 1805040 */

As we saw in section 2, Object Oriented Programming has some useful features that come
in handy in software engineering. But these features alone cannot make any code readable,
reusable and easily maintainable. For example, we can create classes in OOP languages.
Each class can have many variables and methods; there is no bound on them. But, when the
number of variables and methods become too huge, it becomes very difficult to keep track
of them, and, as such, the code becomes hard to read, debug and maintain. Similarly, we
can perform nested branching. However, if the branch becomes too deep, then it becomes
difficult to keep track of the branching conditions and as such, if there is any bug, it is very
hard to debug them. To ensure the readability, re-usability and easy maintenance of the
codebase, there are some basic guidelines that one should follow during writing codes for a
software. These are not any hard-and-fast rules, these are more like thumb-rules that make
the developers’ lives easier. In this section, we will take a look at how design guidelines help
us write clean code.

3.1 Favour Composition over Inheritance


Inheritance is one of the three main features of OOP. It is also a very useful feature, since
it helps us reuse code fragments. But, in order to write good, clean code, sometimes we
are better off not using inheritance. In these cases we use object composition instead.
Inheritance is a relationship whereas composition has a relationship A key motivation
for this is that while using inheritance, we cannot inherit multiple classes. But, if we use
object composition instead of inheritance, then there is no such restriction. We can further
illustrate this using the following examples.

14
Figure 5: Example of Inheritance

In this example, the class Cube extends Rectangle to use its getArea() method.

15
Figure 6: Example of Composition

In this figure, we can achieve the same result as figure 5 by using composition. Here
we have kept an object of the Rectangle2 class in the Cube2 class to use the getArea()
method.

Now, let us create two classes named Summer and Exp, as shown in the following figure.

16
Figure 7: Summer and Exp classes

Next, let us suppose that we want to find the square of the sum of two numbers. To do
this, we require both the getSum() and getExp() methods. We cannot use inheritance to
achieve that, since multiple inheritance is not allowed in Java. But we can easily do that if
we use composition, as shown in the following figure.

17
Figure 8: Example of Composition (2)

As we can see here, using composition is better than using inheritance. There are several
other benefits of using composition over inheritance, including:

• In inheritance, the super class (which we are inheriting from) is defined and cannot
be changed in runtime. Thus, classes and objects created through inheritance are
tightly coupled and changing the parent or superclass in an inheritance relationship
risks breaking the code. Whereas, classes and objects created through composition
are loosely coupled, meaning that we can more easily change the component parts
without breaking the code. Hence, composition offers much more flexibility than
inheritance.

• We can only inherit from one class (in Java and C#), whereas composition allows us
to use functionality from multiple classes.

• Inheritance cannot extend final class, whereas composition allows code reuse even
from final classes.

• In inheritance we need parent class in order to test a child class but composition allows
us to test the implementation of the classes we are using independent of parent or child
classes.

18
This is why, in most cases composition should be used over inheritance. This is a very
common design guideline.

3.2 Program to Interfaces, Not Implementations


3.2.1 Abstract Class vs Interface
Interfaces are everywhere in software engineering. Interfaces are used to achieve abstrac-
tion. We can achieve this abstraction by using abstract classes too. But there are some
differences between these two concepts. Understanding when to use abstract classes and
when to use interfaces and how they behave in runtime is important for writing good, clean
code.

Abstract Method: Methods for which only the signature is given, but not implementa-
tion.

Interface: Interfaces typically can have only abstract methods. Since Java - 8, interfaces
can have default and static methods too. But they cannot have non-abstract non-
static methods, so that classes that implement interfaces are forced to implement the
abstract methods; otherwise they will become abstract classes. Interfaces can have only
static and final variables. Since interfaces mostly have abstract methods, they cannot be
instantiated.

Abstract Class: Abstract class can have both abstract and non-abstract methods. It
can also have final, non-final, static and non-static variables. Since abstract classes will
have at least one abstract method, they cannot be instantiated as well.

When to use which: If we need to have both abstract and non-abstract methods,
we have to use abstract classes. Otherwise, it is better to use interfaces, since a class can
implement multiple interfaces but cannot extend more than one abstract classes.

3.2.2 Design Guideline: Program to Interfaces


This design guideline guides us to make use of abstract types instead of concrete ones.
Program to interfaces actually means writing critical business-logic in a super-type like
an interface or abstract class in java. This way the actual runtime object is not locked
into the code. We can show its significance by the following example.

Let, we are writing an application which has a database accessor layer. We have imple-
mented a ServiceClass which calls a DBClient class. The DBClient class is a concrete
class, programmed to access Postgres DB. Here, the DBClient is a heavy duty class
with all helper methods required to access the DB. Now, if the client decides to switch to
a NoSQL database like MongoDB instead of Postgres DB, or add it as a secondary
database for some specific purposes, then we would have to rewrite the DBClient class
which would be very complicated, and in some cases may break the code.

19
To avoid these situations, these modules should have an abstract super-type like an
interface, as shown in the following figure.

Figure 9: Programming to interfaces instead of implementations

Here, concrete classes like PostgresDBClient and MySQLDBClient implement the


abstract methods provided in the interface/abstract class AbstractDBClient. This way,
depending in the DB type, we can use on or the other, without having to alter the busniess-
logic.

20
4 Importance of Design Principles
/* This section is written by roll 1805041 */

4.1 Overview
In software development, the main challenge isn’t just generating output. Rather the main
challenge is to make one’s code readable for others.
In a typical software project, one has to deal with tens and hundreds of classes. Remem-
bering which class performs what work and which method of which classes, when there is
no structured pattern to your written code, is no easy feat. One must remember that their
code must be easy to read and edit, as software development is done as a team effort, not
just by oneself. Moreover, your code must be eligible for reviews by reviewers.

Always remember, it is easy to write a code computer can understand, but it


is difficult to write code humans can understand.

4.2 Indicators for good code


There are several indicators for good code. Some common ones include:

• Readability: Good code should be easy to read and understand. It should use con-
sistent naming conventions, appropriate comments, and clear and concise logic.

• Modularity: Good code should be modular, with each function or class responsible
for a specific task. This makes the code easier to test, maintain, and modify.

• Scalability: Good code should be scalable, able to handle increasing amounts of data
or traffic without becoming slower or less reliable. It should be designed to be easily
extensible and maintainable.

• Testability: Good code should be testable, with a clear separation between the code
being tested and any external dependencies. It should be designed to be easily testable
using automated unit tests, integration tests, and end-to-end tests.

Some design principles that help coders achieve these are described in the following sections.

5 DRY
/* This section is written by roll 1805041 */

5.1 Introduction
DRY stands for Don’t Repeat Yourself . It is a design principle stating that every piece
of knowledge or functionality in a software system should have a single representation in the
codebase, and that repetition should be eliminated whenever possible.

21
It is the opposite of WET which stands for Write Everything Twice.

The DRY principle is often seen as a way to promote code reusability, maintainability,
and scalability, as it reduces the amount of code that needs to be written, tested, and main-
tained, and also makes it easier to make changes to the codebase without introducing bugs
or inconsistencies.

5.2 Rule of 3
To follow the DRY principle, developers should identify common code patterns or functions
that are used throughout the codebase and factor them out into reusable functions, libraries,
or modules.
In order to do this, whenever a developer copy-pastes any segment of code, he should first
opt for creating abstractions and reusable code instead of copy-pasting. If the code is still
copy-pasted, he should pin the idea in his head that he has done something wrong. When
he goes to copy that code for a third time, he should at once change his code structure to
avoid that copy-paste at all costs.
This is called the Rule of 3 (copy-paste is only okay until the second time, then it must be
resolved any way possible).
By using the rule of 3, developers can reduce code duplication, improve code maintainability,
and make their software systems more scalable and efficient.

5.3 Cases where DRY principle is used


Some cases where DRY principle can be applied are:

• Copy-pasting: If the same code block is used in multiple places in a software system.
The DRY principle suggests that this code should be moved to a single function or
method and called from each location.

• Hardcoded values: Hardcoding values such as file paths, URLs, or database connec-
tion strings etc. The DRY principle suggests that these values should be stored in a
single location, such as a configuration file or database, and accessed from there.

• Repetitive logic: If the same logic is repeated in multiple functions or methods i.e,
evil switch, if-else blocks etc.

5.4 Evil Switch


The Evil Switch is a case, where DRY design principle comes in handy. Let us consider
the following piece of code:

22
Now what if there was another function getBonus like:

In both cases, we can see that the switch condition statements are same.
Now what if there were 20 functions with the same switch conditions? And what if there
were a 100 switch conditions?
Copy-pasting does seem like an easy solution at first glance, but how can you guarantee that
you have correctly copy-pasted all the switch cases? And imagine, you need to add a new
switch case, how can you guarantee you will not miss any of the 20 places you need to add
the new case in?

This kind of switch functions which appear in multiple places in the codebase are known as
evil switch. Evil switches break the DRY principle.

23
5.5 Resolving Evil Switch
Evil switches can easily be resolved to uphold DRY design principle using strategy design
pattern.

6 KISS and YAGNI


/* This section is written by roll 1805042 */

6.1 KISS
KISS stands for Keep It Simple, Stupid . Some also says Keep It Stupid Simple
which means it has to be simple enough that even stupids can understand it.

Sometimes we write a code where everything is fine and it is working perfectly, but the
code reviewer might reject the code because it is not simple enough. Sometimes using recent
features and compact operations like nested ternary to code more concisely can also lead
to this. All this is because these codes are difficult to consume, so they are discouraged in
software design fields.

So, the main idea here is, if the code is not simple, then we have to try to make it sim-
ple. Maybe an example can help us to understand it more clearly. Suppose, we have a
2D array of m*n elements, from where we have to find one. In this case, a very trivial
approach is to go with a nested loop which will give us the time complexity of O(m*n).
Another better approach regarding time complexity will be to use a hashmap or dictionary
data structure which will use a key value maaping. Here we will iterate over n hashtable
and each hashtable might have m elements in it. Time complexity is reduced to O(m+n),
definitely a better and smarter approach. But other than time complexity, there is also a
thin difference between these two approaches - the loop one is easy to understand than the
hashmap one. Now comes the KISS awakening. So, what should we do? The trade off
here is to understand the context first and then use the better strategy. If our data is being
kept in a 10000 * 100 array, then definitely we have to use the hashmap because it is very
obvious. But, if we know that we need a array of 50 * 4 elements, then going for hashing is
unnecessary and complex. We have to understand the context and data behaviour carefully
and then go for the better and simpler approach.

Another idea which also implements KISS is to keep our functions very small. Today our
codes have functions of more than 100-120 lines, but this is highly ¿ discouraged. Critically
saying, a function should not have more than 5-10 lines of code. The very basic elements
in our code is using Ifs and Fors. So we can go like If(condition1) -> function1, else ->
function2, just 4 lines of code.

So, the base line of KISS is we have to understand the context first, and then we have
to implement it in a simpler way.

24
6.2 YAGNI
YAGNI stands for You Ain’t Gonna Need It.

We have learnt a good number of design patterns over time, we know about numerous
Creational Design Pattern, Structural Design Pattern, and Behavioral Design Patterns. So,
after learning all these design patterns, coding using them, understanding their benefits, we
will try to use them very often. But here comes YAGNI, we have to ask ourselves, do we
really need to use a particular pattern here or we are just showing it off?

YAGNI ensures that we are only writing codes that we need. Sometimes it is a wise
decision to think what is going to happen in the future, but this is not the case in Software
Development. We will implement only that part which we need right now, we don’t need to
worry about the near future. Suppose, we are working on a project which will be used be
used by a very limited number of people, and there is no need for future extensions of the
project. YAGNI says that in a project like that, we don’t need microservice architecture
or anything for future modification. We just need to implement it simply and deploy it.

So, YAGNI is all about doing what is obviously needed, no preassumptions, no future
thinking, no overcommit. We will write only what we need right now.

7 Introduction to SOLID
/* This section is written by roll 1805058 */
The SOLID Principles are five principles of Object-Oriented Class Design. They are a
set of rules and best practices to follow while designing a class structure. These principles
were first introduced by the famous Computer Scientist Robert J. Martin (a.k.a Uncle
Bob) in his paper in 2000. But the SOLID acronym was introduced later by Michael
Feathers.

7.1 SOLID means Supple


Let’s think of a scenario where one has built an application completely that took 1 week.
Then suddenly a different requirement arose. Now the code can be changed taking another
whole week. But the demonstration is on the next day. So this brings a huge problem.
The main target of SOLID principles is to build the software in such a way that it is enough
extensible and modifiable that if there arouses a new or different requirement, one can change
the software with minimal effort and without any major harm to the existing system.

25
7.2 What people confuse SOLID with?
7.2.1 Not a Library
A library is a collection of pre-written code that developers can use to perform specific tasks
or functions in their software applications. Libraries can be written in various programming
languages. Such as DLL in C#, jar file in JAVA. SOLID principles do not provide pre-
written code or libraries.

7.2.2 Not a Framework


There are many tasks to implement in Web Development such as how a http request will
arive, how the application will accept that request, how the request will be passed to the
code after receiving, how the object will be created there, how the database will be used,
how the database will give the data, how the response will go to the browser, how will it go
to the mobile application and if there is any error in all these task how will it be handled.
The web applications use frameworks where all these tasks are provided in some placehold-
ers.The placeholders provide necessary information about how to use them. Lots of classes
are used for this purpose. So that there is no need to deal with the infrastructure, network
related complexities or the server related works.
While SOLID is not a framework, it can be used in combination with frameworks to create
high-quality software applications that are robust, scalable, and maintainable.

7.2.3 Not a Pattern


A pattern in software engineering refers to a general, reusable solution to a commonly oc-
curring problem in software design. Patterns provide a way to encapsulate knowledge and
experience in software design, making it easier for developers to create solutions that are
both effective and efficient. While SOLID principles are not a pattern, they can be used
in conjunction with design patterns to create high-quality software systems. By applying
SOLID principles along with design patterns, software developers can create systems that
are both flexible and maintainable, making it easier to modify and extend the system over
time

7.2.4 Not a Goal


The SOLID principles are not something measurable, it’s a qualitative thing. These rules
must not be followed strictly, but rather these are guidelines that help developers make
informed decisions about their software design.
When one has a problem statement of a design pattern, one can absolutely choose a pattern
for the problem. But SOLID isn’t such. It’s not something that can be measured 100%.
One will try to follow these principles as much as possible. And the implementations can be
improved.

26
7.3 SOLID code is Different than typical code
In any typical code there can be an entry point class A, which calls class B, then B calls class
C, C calls class D1. But when a change is required that B should call class D2, then lots
of code lines need to be changed manually. This type of code is rigid which SOLID doesn’t
accept. SOLID uses interfaces to improve the code structure.

In the code which maintains SOLID principles, C will call Interface D, which implements
D1, D2 and many more if needed. Now without any change in the codes of C, the modi-
fication can be made. It can be noticed that in a SOLID maintained code, we can hardly
understand which class calls which one as they use interfaces. This is a major difference
from any typical application. This is another feature of SOLID.

Let’s understand with an example:

A Notification Handler Application receives messages, gives each message an ID and stores
them in a file system as text files. When the messages are needed the message ID is used
to retrieve the message from the file system. The application is fully running and it uses

27
classes including the FILEStorage class.

Now if we want to change the application to store messages in a database instead of file
system, we’ll have to modify a lot of code in the callee MSG class without the help of inter-
face. SOLID tells us to use IStorage which is an Interface that implements FILEStorage,
DatabaseStorage, NoSQLStorage and whatever that can be needed in future. For using
interface, now even if we want to change the file system to a database system, MSG class
need not be modified. Also seeing the code it cannot be understood which class calls which
one.

28
7.4 SOLID helps to avoid
• Rigidity: Rigidity refers to a software system’s inability to adapt to changes. A rigid
system is one where making changes to the code requires extensive modifications to
multiple parts of the system. This can be caused by poor design or lack of modularity,
making it difficult to maintain and extend the system. A rigid system is often a sign of
violating the Open/Closed Principle, where the system is not open for extension and
closed for modification.
• Fragility: A fragile system is one where small changes can cause unexpected problems
in other parts of the system. This can be caused by tight coupling between different
components of the system or a lack of testing. A fragile system can make it difficult
to add new features or fix bugs without introducing new problems. A fragile system
is often a sign of violating the Single Responsibility Principle, where a class has too
many responsibilities and is tightly coupled to other classes.
• Immobility: Immobility refers to a software system’s inability to reuse components
in other parts of the system or in other projects. An immobile system is one where
it’s difficult to extract and reuse components without bringing along unnecessary de-
pendencies or complex configurations. This can be caused by a lack of modularity or
a lack of abstraction. An immobile system can make it difficult to share code or use
existing libraries, leading to duplication of code and increased development time.
• Viscosity: Viscosity refers to a software system’s tendency to favor a ”bad” solution
over a ”good” one. A viscous system is one where it’s easier to take a shortcut or
apply a quick fix rather than following best practices or refactoring the code. This
can be caused by a lack of understanding of the system’s design or by pressure to
deliver quickly. A viscous system can make it difficult to maintain the code and lead
to technical debt over time.
• Needless Complexity Needless complexity refers to complexity that doesn’t provide
any benefit to the system or its users. This can be caused by over-engineering, lack
of simplicity or abstraction, or making the system do more than what is required.
Needless complexity can make the system harder to understand, maintain or use, and
can negatively impact development time, quality, and productivity.

7.5 The five SOLID Principles


The SOLID principles are shortly explained here -
• Single Responsibility Principle (SRP):
This principle states that a class should have only one reason to change. In other words,
a class should have only one responsibility. This makes the code easier to understand,
maintain, and test.
• Open/Closed Principle (OCP):
This principle states that a class should be open for extension but closed for modifica-
tion. In other words, one should be able to add new functionality to a class without

29
changing its existing code. This reduces the risk of introducing new bugs and makes
the code more reusable.

• Liskov Substitution Principle (LSP):


This principle states that objects of a superclass should be replaceable with objects of
a subclass without affecting the correctness of the program. In other words, a subclass
should be able to be used in place of its parent class without causing any unexpected
behavior. This makes the code more flexible and extensible.

• Interface Segregation Principle (ISP):


This principle states that clients should not be forced to depend on interfaces they do
not use. In other words, a class should have only the methods that are relevant to its
behavior. This makes the code easier to understand and reduces the coupling between
different parts of the system.

• Dependency Inversion Principle (DIP):


This principle states that high-level modules should not depend on low-level modules.
Instead, both should depend on abstractions. In other words, the code should depend
on interfaces rather than concrete implementations. This makes the code more modular
and flexible.

By following these principles, developers can create software systems that are more main-
tainable, flexible, and extensible. This can help to reduce the cost and time required for
software development and improve the quality of the resulting software system. The SOLID
principles are not rules that must be followed strictly, but rather guidelines that help devel-
opers make informed decisions about their software design.

More about these principles will be explained in detail in the next lecture.

THE END

30

You might also like