美文网首页互联网科技Java高级架构Java学习笔记
【本人秃顶程序员】Java的SOLID编程原则

【本人秃顶程序员】Java的SOLID编程原则

作者: 本人秃顶程序员 | 来源:发表于2019-02-27 16:37 被阅读1次

    ←←←←←←←←←←←← 快!点关注

    SOLID阐述了五种设计原则,可帮助开发人员轻松扩展和维护软件:

    S - 单一责任原则
    O - 开放原则
    L - Liskov替代原理
    I - 界面隔离原理
    D - 依赖倒置原则

    单一责任原则

    一个类应该有一个,而且只有一个改变的理由。

    一个类应该只有一个责任,这意味着类应该高度凝聚并实现强相关的逻辑。实现功能1和功能2和功能3(依此类推)的类违反了SRP。

    SRP示例:

    // BAD
    public class UserSettingService {
      public void changeEmail(User user) {
        if (checkAccess(user)) {
           //Grant option to change
        }
      }
      public boolean checkAccess(User user) {
        //Verify if the user is valid.
      }
    }
    
    // GOOD
    public class UserSettingService {
      public void changeEmail(User user) {
        if (securityService.checkAccess(user)) {
           //Grant option to change
        }
      }
    }
    public class SecurityService {
      public static boolean checkAccess(User user) {
        //check the access.
      }
    }
    

    SRP味道

    • 单个类中不止一个上下文分隔的代码
    • 测试中的大型setup初始化设置(TDD在检测SRP违规时非常有用)

    SRP好处

    • 负责给定用例的分隔类现在可以在应用程序的其他部分中重用
    • 负责给定用例的分隔类现在可以单独测试

    开/闭原则

    您应该能够扩展类行为,而无需对其进行修改。

    类应该打开以进行扩展并关闭以进行修改。您应该能够扩展类行为而无需修改其实现:

    // BAD
    public class Logger {
      String logging;
      public Logger(String logging) {
        this.logging = logging;
      }
      public void log() {
        if ("console".equals(logging)) {
          // Log to console
        } else if ("file".equals(logging)) {
          // Log to file
        }
      }
    }
    
    // GOOD
    public interface Log {
        void log();
    }
    public class ConsoleLog implements Log {
      void log() {
        // Log to console
      }
    }
    public class FileLog implements Log {
      void log() {
        // Log to file
      }
    }
    public class Logger {
      Log log;
      public Logger(Log log) {
        this.log = log;
      }
      public void log() {
        this.log.log();
      }
    }
    

    OCP代码味道:

    • 如果你注意到类X直接引用其代码库中的其他类Y,则表明类Y应该传递给类X(通过构造函数/单个方法),例如通过依赖注入
    • 复杂的if-else或switch语句

    OCP好处:

    • 使用封装在单独类中的新功能可以轻松扩展X类功能,而无需更改类X实现(它不知道引入的更改)
    • 代码松散耦合
    • 注入的Y类可以在测试中轻易模拟

    利斯科夫替代原则

    派生类必须可替代其基类。

    这是开/闭原则的​​延伸。派生类不应更改基类的行为(继承方法的行为)。如果类Y是类X的子类,则任何引用类X的实例也应该能够引用类Y(派生类型必须完全替代它们的基类型)。

    // BAD
    public class DataHashSet extends HashSet {
      int addCount = 0;
      public boolean function add(Object object) {
        addCount++;
        return super.add(object);
      }
      // the size of count will be added twice!
      public boolean function addAll(Collection collection) {
        addCount += collection.size();
        return super.addAll(collection);
      }
    }
    
    // GOOD: Delegation Over Subtyping
    public class DataHashSet implements Set {
      int addCount = 0;
      Set set;
      public DataHashSet(Set set) {
        this.set = set;
      }
      public boolean add(Object object) {
        addCount++;
        return this.set.add(object);
      }
      public boolean addAll(Collection collection) {
        addCount += collection.size();
        return this.set.addAll(collection);
      }
    }
    

    LSP代码味道:

    • 如果它看起来像一只鸭子,嘎嘎叫像鸭子但需要电池才能达到这个目的 - 这可能违反了LSP
    • 修改子类中的继承行为
    • 在重写的继承方法中引发的异常

    LSP好处:

    • 避免意外和不正确的结果
    • 明确区分共享继承接口和扩展功能

    接口隔离原理

    制作客户端特定的细粒度接口。

    一旦接口变得太大/太胖,我们绝对需要将其拆分为更具体的小接口。接口将由将使用它的客户端定义,这意味着接口的客户端将只知道与它们相关的方法。

    // BAD
    public interface Car {
      Status open();
      Speed drive(Gas gas);
      Engine changeEngine(Engine newEngine);
    }
    public class Driver {
      public Driver(Car car) {}
      public Speed ride() {
        this.car.open();
        return this.car.drive(new Gas(10));
      }
    }
    public class Mechanic {
      public Mechanic(Car car) {}
      public Engine fixEngine(Engine newEngine) {
        return this.car.changeEngine(newEngine);
      }
    }
    
    // GOOD
    public interface RidableCar {
      Status open();
      Speed drive(Gas gas);
    }
    public interface FixableCar {
        Engine changeEngine(Engine newEngine);
      }
    public class Driver {
      // Same with RidableCar
    }
    public class Mechanic {
      // Same with FixableCar
    }
    

    ISP代码味道

    • 由许多类实现的一个胖接口,其中没有一个类实现100%的接口方法。这种胖接口应该分成适合客户需求的较小接口

    ISP好处

    • 高度凝聚力的代码
    • 避免使用单个胖接口在所有类之间进行耦合(一旦单个胖接口中的方法得到更新,所有类 - 无论是否使用此方法 - 都被迫相应地更新)
    • 通过将职责分组到单独的界面中,明确分离业务逻辑

    依赖倒置原则

    依赖于抽象,而不是实现

    如果您的实现细节将取决于更高级别的抽象,它将帮助您获得正确耦合的系统。而且,它将影响该系统的封装和内聚。

    // BAD
    public class SQLDatabase {
      public void connect() {
        String connectionstring = System.getProperty("MSSQLConnection");
        // Make DB Connection
      }
      public Object search(String key) {
        // Do SQL Statement
        return query.find();
      }
    }
    public class DocumentDatabase {
      // Same logic but with document details
    }
    
    // GOOD
    public interface Connector {
      Connection open();
    }
    public interface Finder {
      Object find(String key);
    }
    public class MySqlConnector implements Connector {}
    public class DocumentConnector implements Connector {}
    public class MySqlFinder implements Finder {}
    public class DocumentFinder implements Finder {}
    
    public class Database {
      public Database(Connector connector,
                      Finder finder) {
        this.connector = connector;
        this.finder = finder;
      }
      public Connection connect() {
        return connector.open();
      }
      public Object search(String key) {
        return finder.find(key);
      }
    }
    

    DIP味道:

    • 在高级模块中实例化低级模块
    • 调用低级模块/类的类方法

    DIP好处:

    • 通过使更高级别的模块独立于较低级别的模块来提高其可重用性
    • 可以采用依赖性注入1来促进所选择的低级组件实现的运行时供应到高级组件
    • 注入类可以在测试中轻易模拟

    写在最后:

    秃顶程序员的不易,看到这里,点了关注吧!
    点关注,不迷路,持续更新!!!

    相关文章

      网友评论

        本文标题:【本人秃顶程序员】Java的SOLID编程原则

        本文链接:https://www.haomeiwen.com/subject/onpfuqtx.html