SOLID trong Android

Hẳn các bạn không còn lạ lẫm với 5 principles của OOP. Hôm nay chúng ta sẽ show 1 số ví dụ để xem nó apply vào Android như thế nào.

Trước hết, xin nhắc lại S.O.L.I.D biểu thị cho:

S — Single Responsibility Principle

O — Open Closed Principle

L — Liskov Substitution Principle

I — Interface Segregation Principle

D — Dependency Inversion Principle

Và chúng ta sẽ đi vào từng phần ngay sau đây:

1. Single Responsibility Principle

**A class should have only one reason to change.**

Giống như tên gọi, mỗi một class/module chỉ thực hiện một chức năng. Nếu class của bạn có nhiều hơn 1 nhiệm vụ, hãy tách các function đó sang 1 class khác.

😡Bad code:

public class User {

    private String firstName;
    private String lastName;
    private int rating;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public int getRating() {
        return rating;
    }

    public void setRating(int rating) {
        this.rating = rating;
    }

    public boolean isVipUser() {
        return rating == 5;
    }
}

😊Good code:

public class User{

    private String firstName;
    private String lastName;
    private int rating;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public int getRating() {
        return rating;
    }

    public void setRating(int rating) {
        this.rating = rating;
    }
}

public class UserType extends User{
    public boolean isVipUser() {
        return this.user.getRating() == 5;
    }
}

2. Open Closed Principle

**Software entities like classes, modules and functions should be open for extension but closed for modifications.**

Chúng ta phải viết code để có thể thích nghi với các yêu cầu mới mà không thay đổi code cũ. Ở đây, chúng ta tập trung vào việc mở rộng các function bằng cách sử dụng interface.

😡Bad code:

public class Circle {
    public double radius;
}
public class Rectangle {
    public double length;
    public double width;
}
public class AreaManager {
    public double getRectangleArea(Rectangle rectangle) {
        return rectangle.length * rectangle.width;
    }

    public double getCircleArea(Circle circle) {
        return Math.PI * circle.radius * circle.radius;
    }
}

😊Good code:

public interface Shape{
    public double getArea();
}

public class Rectangle implements Shape{
    double length;
    double width;
    public double getArea(){
        return length * width;
    }
}

public class Circle implements Shape{
    public double radius;
    public double getArea(){
        return Math.PI *radius*radius;
    }
}
public class AreaManager {
    public double getShapeArea(Shape shape){
        return shape.getArea();
    }
}

3. Liskov Substitution Principle

**Objects in a program should be replaceable with instances of their subtypes without changing the behaviour of that program.**

Ý tưởng chung của nguyên tắc này là các đối tượng có thể được thay thế bởi các instance của subtype của nó, mà không ảnh hưởng đến chức năng hoặc tạo ra bugs.

😡Bad code:

public class Toy {
    private String color;

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public void fly(){
        System.out.println("The toy is flying...");
    }
}
public class Plane extends Toy {
    @Override
    public void fly() {
        super.fly();
        System.out.println("The plane toy is flying...");
    }
}
public class Car extends Toy {
    @Override
    public void fly() {
        //we have an issue...
        super.fly();
    }
}

😊 Good code:

public class Toy {
    private String color;

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}
public class FlyingToy extends Toy {
    public void fly(){
        System.out.println("The toy is flying...");
    }
}
public class LandToy extends Toy {
    public void move(){
        System.out.println("The toy is moving...");
    }
}
public class Plane extends FlyingToy {
    @Override
    public void fly() {
        super.fly();
        System.out.println("The plane toy is flying...");
    }
}
public class Car extends LandToy {
    @Override
    public void move() {
        super.move();
        System.out.println("The car is moving...");
    }
}

4. Interface Segregation Principle

Classes that implement interfaces, should not be forced to implement methods they do not use.

Chúng ta có nhiều interface sẽ tốt hơn là ít interface chứa nhiều function. Như vậy, chúng ta sẽ không cần phải implement các method không cần thiết.

😡Bad code:

public interface Toy {
    void move();
    void fly();
}
public class ToyHouse implements Toy {
    String color;
   
    @Override
    public void setColor(String color) {
        this.color=color;
    }
    @Override
    public void move(){}
    @Override
    public void fly(){}
}

😊Good code:

public interface Toy {
    void setColor(String color);
}
public interface Movable {
    void move();
}
public interface Flyable {
    void fly();
}
public class ToyHouse implements Toy {
    String color;

    @Override
    public void setColor(String color) {
        this.color = color;
    }
}
public class ToyRobot implements Toy, Movable {
    String color;

    @Override
    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public void move() {
        System.out.println("ToyRobot: Start moving robot.");
    }
}

5. Dependency Inversion Principle

High level modules should not depend on low level modules rather both should depend on abstraction. Abstraction should not depend on details; rather detail should depend on abstraction

Dựa trên nguyên tắc này các interface nên phụ thuộc vào các interface khác. Các module sẽ giao tiếp với nhau thông qua interface.

😡Bad code:

public class VEngine {
 }

public class ToyCar {
    VEngine vEngine;
    ToyCar(){
        vEngine = new VEngine();
    }
}

😊Good code:

public interface IEngine {
}

public class VEngine implements IEngine{
    double cylinderCapacity;
}

public class ToyCar {
    IEngine iEngine;
    ToyCar(IEngine engine){
        iEngine = engine;
    }
}