这代码,够优雅!

2023-12-01 13:02:21 浏览数 (1)

引言

最近公司团队每两周进行一次Code Review,了不起心里有点慌,毕竟平常都不注重代码的开发规范,更别说代码的可读性、可维护性了,心里想着就是能跑起来就行。这不,偷偷做了点关于代码规范和编程原则的功课,暗地里把公司的代码重构了一遍,避免在Code Review时被领导喷。本文将会介绍一些编程设计原则,以帮助各位好汉编写出更健壮、可维护的代码。

SOLID 原则

单一职责原则 (SRP)

单一职责原则要求一个类应该只有一个引起它变化的原因。这意味着一个类应该只负责一项职责。

代码语言:javascript复制
// 不遵循:Order类承担了两个职责:计算订单总金额和打印发票。这违反了SRP
public class Order {
    public void calculateTotal() {
        // 计算订单总金额
        // 同时处理打印发票逻辑
    }
}

// 遵循:将计算订单总金额和打印发票的职责分离成两个类,分别是Order和InvoicePrinter,遵循了SRP
public class Order {
    public void calculateTotal() {
        // 计算订单总金额
    }
}

public class InvoicePrinter {
    public void printInvoice(Order order) {
        // 打印发票
    }
}

开闭原则 (OCP)

开闭原则要求软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。这意味着我们应该能够在不修改现有代码的情况下扩展软件的功能。

代码语言:javascript复制
// 不遵循:添加新的形状需要修改Shape类的现有代码,违反了OCP
public class Shape {
    public double calculateArea(String shapeType, double value) {
        if (shapeType.equals("circle")) {
            // 计算圆形面积
        } else if (shapeType.equals("square")) {
            // 计算正方形面积
        }
    }
}


// 遵循:我们可以轻松地添加新的形状类,而不需要修改Shape接口或现有的Circle和Square类,这符合OCP
public interface Shape {
    double calculateArea();
}

public class Circle implements Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    public double calculateArea() {
        return Math.PI * radius * radius;
    }
}

public class Square implements Shape {
    private double side;

    public Square(double side) {
        this side = side;
    }

    public double calculateArea() {
        return side * side;
    }
}

里氏替换原则 (LSP)

里氏替换原则要求子类能够替代其基类,而不引起不一致性。这意味着在任何需要基类的地方,都可以使用其子类来替代。

代码语言:javascript复制
// 不遵循:Ostrich是Bird的子类,但它覆盖了父类的飞行方法并将其留空。这违反了LSP,因为调用Ostrich的fly方法将导致不符合预期的行为。
public class Bird {
    public void fly() {
        // 飞行操作
    }
}

public class Ostrich extends Bird {
    // 鸵鸟不会飞行
}

// 遵循:遵循了LSP,子类Ostrich不再需要实现无意义的飞行操作
public abstract class Animal {
    public void eat() {
        // 吃
    }
}

public interface Bird {
    void fly();
}

public class Sparrow extends Animal implements Bird {
    @Override
    public void fly() {
        // 麻雀的飞行
    }
}

public class Ostrich extends Animal {
    // 鸵鸟不会飞行,继承自Animal,不再实现Bird接口
}

接口隔离原则 (ISP)

接口隔离原则要求客户端不应该强制依赖于其不使用的接口。这意味着接口应该相对小而专注,不应该强迫客户端实现不需要的方法。

代码语言:javascript复制
// 不遵循:Waiter类不需要实现不相关的eat方法,这违反了ISP
public interface Worker {
    void work();
    void eat();
}

public class Engineer implements Worker {
    public void work() {
        // 工程师的工作
    }
    
    public void eat() {
        // 工程师的用餐
    }
}

public class Waiter implements Worker {
    public void work() {
        // 服务员的工作
    }
    
    public void eat() {
        // 服务员不需要用餐
    }
}

// 遵循:将接口拆分成更小的接口Worker和Eater,以遵循ISP
public interface Worker {
    void work();
}

public interface Eater {
    void eat();
}

public class Engineer implements Worker, Eater {
    public void work() {
        // 工程师的工作
    }
    
    public void eat() {
        // 工程师的用餐
    }
}

public class Waiter implements Worker {
    public void work() {
        // 服务员的工作
    }
}

依赖倒置原则 (DIP)

依赖倒置原则要求高层模块不应该依赖于底层模块,二者都应该依赖于抽象。此原则强调了使用接口或抽象类来实现松耦合。

代码语言:javascript复制
// 不遵循:Switch类依赖于具体的LightBulb类,违反了DIP。这会使得Switch和LightBulb之间存在较高的耦合度,当需要替换或扩展不同类型的灯泡时,可能需要修改Switch类的代码。
public class LightBulb {
    public void turnOn() {
        // 打开灯泡
    }

    public void turnOff() {
        // 关闭灯泡
    }
}

public class Switch {
    private LightBulb bulb;

    public Switch(LightBulb bulb) {
        this.bulb = bulb;
    }

    public void operate() {
        if (/* 检查条件 */) {
            bulb.turnOn();
        } else {
            bulb.turnOff();
        }
    }
}

// 遵循:Switch类依赖于抽象的Switchable接口,而不依赖于具体的LightBulb或Fan类。这遵循了DIP,使得Switch类更加灵活,可以操作各种实现了Switchable接口的设备,而不需要修改Switch类的代码。这降低了耦合度,提高了可维护性。
public interface Switchable {
    void turnOn();
    void turnOff();
}

public class LightBulb implements Switchable {
    public void turnOn() {
        // 打开灯泡
    }

    public void turnOff() {
        // 关闭灯泡
    }
}

public class Fan implements Switchable {
    public void turnOn() {
        // 打开风扇
    }

    public void turnOff() {
        // 关闭风扇
    }
}

public class Switch {
    private Switchable device;

    public Switch(Switchable device) {
        this.device = device;
    }

    public void operate() {
        if (/* 检查条件 */) {
            device.turnOn();
        } else {
            device.turnOff();
        }
    }
}

迪米特法则 (LoD)

迪米特法则(Law of Demeter,LoD)要求一个对象应当尽可能减少与其他对象之间的交互,只与其直接的朋友(直接依赖)通信。这有助于降低系统中对象之间的耦合度,提高代码的可维护性。

代码语言:javascript复制
public class School {
    private List<Student> students;

    public School() {
        students = new ArrayList<>();
    }

    public void admitStudent(Student student) {
        students.add(student);
    }

    public void conductExams() {
        for (Student student : students) {
            student.takeExam();
        }
    }
}

public class Student {
    private String name;

    public Student(String name) {
        this.name = name;
    }

    public void takeExam() {
        // 学生参加考试
    }
}

public class Teacher {
    private School school;

    public Teacher(School school) {
        this.school = school;
    }

    public void startExams() {
        school.conductExams();
    }
}

Teacher类只与其直接的朋友School类通信,而不需要直接与Student类交互。这遵循了迪米特法则,降低了耦合度。

其他编程原则

"Tell, Don't Ask" 原则

代码语言:javascript复制
// 不遵循:ShoppingCart类不遵循"Tell, Don't Ask"原则,因为它在添加商品时首先查询商品的状态,然后决定是否添加。这会导致较高的耦合度和不够清晰的代码。
public class ShoppingCart {
    private List<Item> items = new ArrayList();

    public void addItem(Item item) {
        if (item.isAvailable()) {
            items.add(item);
        }
    }
}

// 遵循:ShoppingCart类遵循"Tell, Don't Ask"原则,它直接告诉商品对象要添加到购物车,而不再查询商品的状态。这种方式降低了耦合度,使代码更加清晰和可维护。
public class ShoppingCart {
    private List<Item> items = new ArrayList();

    public void addItem(Item item) {
        item.addToCart(this);
    }
}

public class Item {
    private boolean available;

    public void addToCart(ShoppingCart cart) {
        if (isAvailable()) {
            cart.add(this);
        }
    }

    private boolean isAvailable() {
        return available;
    }
}

结论

遵循SOLID原则以及其他编程原则,是编写出高质量、可维护代码的关键。这些原则有助于减少代码中的重复、降低耦合度、提高扩展性和可读性。我们在日常的开发中应当积极应用这些原则,以创建更可靠的软件系统。

我是了不起
和我一起学习更多精彩知识!!!

0 人点赞