Ads

Tuesday, 9 November 2021

SOLID Principles

SOLID principles are the basic essential thing to know for every developer before he/she starts coding in any IDE.

Here is the full form

S - Single Responsibility Principle

- While developing we should always try to achieve high level cohesion.

Ex: Lets say, we have a class Square and there are four methods which is as follows

class Rectangle {

func caluclateArea() { ... }

func caluclatePerimeter() { ... }

func draw() { ... }

func rotate() { ... }

}

If we observe the above class, we can say that 'caluclateArea' and 'caluclatePerimeter' are related and 'draw' and 'rotate' are related. But 'caluclateArea' and 'draw' are not related to anything. Therefore we can say the level of Cohesion is low.

So what can we do here is, we can splint the code as follows.

class Rectangle {

func caluclateArea() { ... }

func caluclatePerimeter() { ... }

}

class RectangleUI {

func draw() { ... }

func rotate() { ... }

}

If we observe above, I have divided the code into Rectangle and RectangleUI. 

RectangleUI handle the responsibility of UI update and Rectangle handles the responsibility of calculation. Now we can say that the level of Cohesion is high.

- While developing the code we should always try to do loose coupling.

Definitions

1. Cohesion - Cohesion is the degree to which the various parts of the software components are related.

2. Coupling - Coupling is defined as the level of inter dependency between various software components.

Notes:

Software always changes with the time. And time is money. If any software component holds the single responsibility principle, changes can made easy so always consider the below points before starting developing anything.

1.  The changes required for the software component should have only one reason to change / modify.

2. Every Software component should handle one and only responsibility for the same reason above.


O - Open Close Principle

- Software component should be closed for modification, but open for extension.

The meaning of closed for modification, but open for extension is as follows.

1. New features adding to the software component, shouldn't have to modify the existing functionality.(Which we can say as "Closed for modification").

2. A software component should be extendable to add a new feature or to add a new behaviour to it.

Ex:

class Flat {

let ownerName: String

let flatNumber: Int

init(ownerName: String, flatNumber: Int) {

self. ownerName = ownerName

self.flatNumber = flatNumber

}

class Apartment {

var flats: [Flat]

init(flats: [Flat]) {

self.flats = flats 

func addFlat(flat: Flat)) {

flats.append(flat)

}

Usage as follows:

let app = Apartment(flats: [])

app.append(flat: <someValue>)

Now If I introduce new type of Flats, lets say NewFlats. The above code will not work.

If we observe the above code, if we change the name of the Flat to NewFlats or want to introduce new functionality, we have to modify the code in the Apartment classes. Which means the code should be open for modification, therefore it doesn't follow the OCP.

Therefore now I am changing the above code as follows to follow OCP.

protocol ResidentialFlats {}

class Flat: ResidentialFlats {

let ownerName: String

let flatNumber: Int

init(ownerName: String, flatNumber: Int) {

self. ownerName = ownerName

self.flatNumber = flatNumber

}

class Apartment {

var flats: [ResidentialFlats]

init(flats: [ResidentialFlats]) {

self.flats = flats 

func addFlat(flat: ResidentialFlats)) {

flats.append(flat)

}

Usage as follows:

let app = Apartment(flats: [])

app.append(flat: <someValue type 'Flat'>)


Now If I introduce new type of Flats

 class NewFlat: ResidentialFlats {

let ownerName: String

let flatNumber: Int

let isCornerFlat: Bool

}

Usage as follows:

let app = Apartment(flats: [])

app.append(flat: <someValue type 'NewFlat'>)

The above code still works without any issue. We can say that now 'Apartment' class is closed for modification and open for extension.

L - Liskov Substitution Principle

- Objects should be replaceable with their  subtypes without effecting  the correctness of the program.

Lets explain with an example.

class Rectangle {

var width: Int

var height: Int

 init(width: Int, height: Int) {

self.width = width

self.height = height

}  

func area() -> Int{

return  width * height

}

}

class Square: Rectangle {

ovveride var width: Int {

didSet {

super.height = width

ovveride var height: Int {

didSet {

super. width = height

}

}

let square = Square(width: 10, height: 10)

let rectangle: Rectangle = square

rectangle.height = 7

rectangle.widht = 5

rectangle.area() // This value will be 25 instead 35


What is happening here is, we are creating Rectangle attaching the reference of the Square, which is Polymorphism. So the actual reference will be pointed to the Square, therefore when we call the area(), the value we get will be the result of Square instead of Rectangle.

So according to the LSP, even though we change the subclass type, it should not effect the program. To achieve this, we have to modify the above code as follows.


protocol Shape {

func area() -> Int

}

class Rectangle: Shape {

var width: Int

var height: Int

 init(width: Int, height: Int) {

self.width = width

self.height = height

}  

func area() -> Int{

return  width * height

}

}

class Square: Shape {

var side: Int 

init(side: Int) {

self.side = side

}   

func area() -> Int {

return  side * side

}

let square: Shape = Square(side: 10)

//let rectangle: Rectangle = square// This will throw compile time error, because Rectangle and Square are 2 different types.

So we can use it as 

let rectangle: Shape = Rectangle(width: 10, height: 12)

print(rectangle.area())

I - Interface Segregation Principle

- The name it self suggest the interface segregation. No client should be forced to use unwanted methods.

Example:

protocol XeroxMachine {

func print()

func scan()

func fax()

}

class MultiFunctionMachine: XeroxMachine {

func print() { ... } 

func scan() { ... }

func fax() { ... }

}

class PrintAndScanMachine: XeroxMachine {

func print() { ... } 

func scan() { ... }

func fax() {// Empty}

}

class FaxMachine: XeroxMachine {

func print() { // Empty  } 

func scan() { // Empty }

func fax() { ... }

}

If we observe the above example, FaxMachine class have the empty methods scan and print because we are using XeroxMachine protocol. Similarly different classes uses different mandatory methods that are required and we have some empty functions. If any other classes call this empty methods, it doesn't do anything. 

According to the ISP, we shouldn't have these kind of empty methods, therefore I am going to change the above code as follows.

protocol PrintScanProtocol {

func print()

func scan()

}

protocol FaxProtocol {

func fax()

}

class MultiFunctionMachine: PrintScanProtocol,  FaxProtocol {

func print() { ... } 

func scan() { ... }

func fax() { ... }

}

class PrintAndScanMachine: PrintScanProtocol {

func print() { ... } 

func scan() { ... }

}

class FaxMachine: FaxProtocol {

func fax() { ... }

}

Identification of Violations:

1. Fat Protocols/ Interface -> Follow SRP, every component should have one and only one responsibility.

2. Low cohesion ->  Again Follow SRP, try to achieve high cohesion.

3. Empty methods -> Again SRP, loose coupling.

D - Dependency Inversion Principle

- High-level modules doesn't depend on low level modules. Both should depend on abstraction.
- Abstraction doesn't depend on details, but details should depend on the abstraction.

Ex:

struct Product {
var id
var name
}
class ProductCatalog {
func getProducts() -> [Products] {
// Returns the products having in the catalog
}

class ProductListScreen {
var products: [Products]?
var productCatalog: ProductCatalog?

func getProducts() {
productCatalog = ProductCatalog()
products = productCatalog.getProducts()
}
}

If we observe the above code, the high level code will be ProductListScreen (because it is directly interact with the UI) and ProductCatalog is the low level code. In the above ProductListScreen directly depends  on ProductCatalog for fetching the products.

But according to the DIP, high level and low level should depend on the abstraction, not on anyone of them. So now I am going to change the code as follows.

protocol ProductProtocol {
var id: String { get set }
var name: String { get set }
}

protocol ProductCatalogProtocol {
func getProducts() -> [ProductProtocol]

struct Product: ProductProtocol {
var id: String
var name: String
}

class ProductCatalog: ProductCatalogProtocol {
func getProducts() -> [ProductProtocol] {
// Returns the products having in the catalog
}

class ProductListScreen {
var products: [ProductProtocol]?
var productCatalog: ProductCatalogProtocol?

init(....) {

....

}

func getProducts() {
products = productCatalog.getProducts()
}
}

If we observe the above code, the high level code ProductListScreen and the low level code ProductCatalog depends on ProductCatalogProtocol which is an abstraction.

 Here are the main terminologies which we will use to achieve DIP.
1. Dependency Injection
2. Inversion of control

Monday, 23 August 2021

Home

Hi Guys, This page is mainly for the purpose for sharing the knowledge what I have been learning and learned.

I will try to put all the things that I have learned in my total experience.

Since I am a self learner there might be some mistake that I will be doing so please don't mind and let me know if I am doing anything wrong

Thanks for the support guys

- SnehaVik

Index of this blog

 

Flutter Setup In Mac

 Hi Guys, 

I have mac machine, so I am going to tell you how to install flutter in MAC machine.

  • Download Flutter Stable SDK version and save in you machine which is required while setting the IDE. I prefer it to be saved in 'Documents' folder.
    1. In my case I have saved it in 'Documents/SDKs'
    2. Unzip the Flutter Stable SDK zip file
    3. Open terminal and type the following commands
      1. $ cd ~/Documents/SDKs/flutter
      2. $ export PATH="$PATH:`pwd`/flutter/bin"
    4. The Step 3 is for the particular terminal window only, to make it permanent follow the below process in the terminal
      1. $ echo $SHELL 
      2. The above command will tell you what kind of shell is your macbook is. In my case it is Z shell
      3. $ export PATH="$PATH:[PATH_OF_FLUTTER_GIT_DIRECTORY]/bin"
      4. To verify use command $ echo $PATH
      5. To verify the flutter is working use the command $which flutter
    5. The above command should provide with the path.
  • Once the SDK setup is completed, we have to setup the IDE. Below is the process to setup.
    1. Download the xcode and command line tools to run on iOS device/ Simulator.
    2. To run on Android device, follow the below process.
      1. Download and install Android studio.
      2. Once Android studio installed, go to the Android studio setup wizard and install 
        1. Latest Android SDK
        2. Latest Android SDK command line tools
        3. Latest Android SDK build tools
      3. Open Preference from the Android studio, go to Plugins and search for "Flutter". If it is not installed, install it. 
      4. Similarly, install Dart and Kotlin.
  • Once the IDE and SDK setup is completed, check what is the version of JAVA that is active. For flutter to work JAVA 8 should be active. If Java 8 is not installed, install Java 8 and do the following
    1. Open terminal and follow the commands
      1. $ nano ~/.zhenv
      2. Z Shell file will opened and enter the below text
      3. export JAVA_HOME=$(/usr/libexec/java_home -v1.8)
  • Finally, run the command in the terminal $ flutter doctor
    • If you get any error related to "Android license status unknown", Do the following
    • In terminal, type the command $ flutter doctor --android-licenses
    • Then it ask to accept the licenses, please accept and continue.

Saturday, 23 January 2021

Planning in November with Kids in India

 Hi Guys,

I have a kid who will be 11 months old by November. 

I know that November is cold time in India, but my one and only love my wife was born and I always plan that day.

Now we have one little special one with us so I thought we have to plan it properly before anything, so he will be safe and comfortable.

So my research goes this way.

During November month it is cold in India, so I found below 10 places are the best to visit in my option.


10. Udaipur, Rajasthan

This is my one of the favorite place to visit. We have many temples, shooting spots, ghates and magnificient Palace. The best part is standing opposite side of the lake palace and capturing some mesmorising photos.

9. Rann of Kutch, Gujarat

This is the place where SUN, SAND and OCEAN meets. Its beautiful to watch sunset.

8. Rishikesh, Uttarakhand

There is one statement to explain this place. "It is in the foot of Himalayas and beside holy river Ganga". 

7. Coorg, Karnataka

This place is called Scotland of India. The most important attraction here is majestic waterfalls.

Ex: Abbey Falls, Mallalli Waterfalls.

6. Goa, Goa

During start of November there will be many events that happens in Goa and will continue until end of January mostly. Most parting place, but there are so many beautiful locations present to watch.

5. Munnar, Kerela

The best to visit this place is during December but I still perefer to go to this place enjoy the beautiful location and climate. And moreover during November the crowd will be not too much. 

/*Still Researching will update once find more*/ 

SOLID Principles

SOLID principles are the basic essential thing to know for every developer before he/she starts coding in any IDE. Here is the full form S ...