软件设计原则-iOS

2023-06-09  本文已影响0人  Sweet丶

最近在搞代码重构,这是一个很好的学习软件设计原则、设计模式、架构设计并实践的机会,本文是以一个iOS开发人员对软件设计原则的一个概括总结。

一、概况

软件设计原则和设计模式是紧密相关的两个概念,但它们有着不同的焦点和目的。软件设计原则主要关注如何设计“好的”软件,强调的是架构设计方面的规范和指导思想;而设计模式则是针对具体的问题和场景,提供精细的解决方案,这些方案包含了具体实现细节和代码结构。
软件设计原则是指导软件开发的通用规范和指导思想,是从更高层面上指导软件设计的。常见的软件设计原则有SOLID原则、KISS原则、DRY原则、YAGNI原则等等。

下面来对具体的"原则"做个了解.

二、SOLID原则

三、迪米特法则(Law of Demeter)

也称最少知道原则,Least Knowledge Principle, LKP
指一个对象应该对其他对象保持最少的了解,尽量降低类与类之间的耦合度。一个类应依赖于那些最直接合理的类,而不是依赖于很多其他类。主要是:

  1. 一个对象只应该调用直接朋友(即与其它对象有直接关系的对象)的方法。在对象之间建立一条明确的通信路径可以降低耦合度,使系统更容易维护和修改。
  2. 谨慎使用外观模式: 外观模式可以帮助我们降低耦合性,但是在使用时需要注意,放置太多的业务逻辑代码到外观模式中会导致对象之间互相依赖,不利于扩展和维护。
  3. 不暴露任何细节:一个好的设计应该将系统的实现细节封装在类内部,不向外暴露任何不必要的细节信息。这样可以降低类之间的耦合度,使系统更加稳定和灵活。
    例如:在iOS开发中,UIView只知道与其相关联的UIViewController,而不需要知道UIViewController背后的一层层业务逻辑和数据存储的实现,以此来实现类间的解耦合。另外一个例子是KVO.

四、合成复用原则(Composite/Aggregate Reuse Principle)

用组合和聚合关系来代替继承实现代码复用。这里的组合指的是通过将一个或多个对象(组合部分)组合成一个更大的、有着更高层次抽象的整体(组合整体),而聚合则是指在一个类中引用另一个类的实例。

五、KISS原则(Keep It Simple And Stupid)

KISS原则强调在设计软件时应力求简单,避免复杂和不必要的细节和冗余,以保持代码的简洁、易理解、可维护和可扩展。

六、YAGNI原则(You Ain't Gonna Need It)

YAGNI原则是一种迭代开发和极限编程中的设计哲学,它告诉程序员不要在软件中添加除了当前需要之外的任何功能,避免浪费时间和精力开发无用功能,或在被证明是需要之前预测未来的需求。

七、DRY原则(Don’t Repeat Yourself)

DRY原则要求程序员避免代码重复,避免重复造轮子,复用已有的模块和代码。这可以帮助减少错误率,提高代码的可读性、可维护性和可扩展性。

八、GRASP原则(General Responsibility Assignment Software Parren)

职责分配软件模式,GRASP原则提供了一些模式和约束条件,帮助程序员正确分配和选择各个类和对象所应负责的职责,以提高代码的可读性、可维护性和可扩展性。主要有以下九个


下面是对其中几个不太好理解的原则的举例说明:

1. 接口隔离原则的实例

一个常见的应用场景是网络请求,我们可以通过封装一个网络请求库来方便地对外提供网络请求的功能。在实现网络请求库时,我们可以应用接口隔离原则,将网络请求接口拆分成更加细粒度的接口,如下所示:

protocol NetworkRequestProtocol {
    associatedtype ResponseDataType
    
    func get(url: URL, parameters: [String: Any]?,
             completion: ((Result<ResponseDataType, NetworkError>) -> Void)?)
    
    func post(url: URL, parameters: [String: Any]?,
              completion: ((Result<ResponseDataType, NetworkError>) -> Void)?)
}

protocol NetworkRequestConfigurableProtocol {
    func setHTTPHeaderFields(_ headers: [String: String])
    func setSerializationType(_ type: NetworkRequestSerializationType)
}
2. 间接性(Indirection)的实例

使用了MusicProvider这个中间对象作为间接层。通过使用 MusicProvider对象,我们可以强制对歌曲访问进行验证,而不会直接使用 AudioPlayer 对象,从而实现 Indirection 原则.

protocol MusicProviderProtocol {
    func playSong(_ song: Song, completionHandler: @escaping (Error?) -> Void)
}

class MusicProvider: MusicProviderProtocol {

    let audioPlayer: AudioPlayerProtocol
    let accessManager: AccessManagerProtocol

    init(audioPlayer: AudioPlayerProtocol, accessManager: AccessManagerProtocol) {
        self.audioPlayer = audioPlayer
        self.accessManager = accessManager
    }

    func playSong(_ song: Song, completionHandler: @escaping (Error?) -> Void) {
        guard accessManager.hasAccess(to: song) else {
            // 提示用户登录或升级账户以获得足够的访问权限
            completionHandler(MyErrors.insufficientAccess)
            return
        }

        audioPlayer.play(song) { error in
            completionHandler(error)
        }
    }
}
3.受保护的变化(Protected Variations)原则的实例

假设我们正在开发一个iOS应用程序,该应用程序包含一个视频播放器功能。我们希望能够将不同的视频播放程序集成到应用程序中,例如AVFoundation或第三方播放器SDK等。为了实现这个目标,我们可以通过下面的方式来应用Protected Variations原则:

我们创建一个名为VideoPlayerProtocol的协议,它定义了播放器的基本行为和接口。在具体的播放器类中,我们将实现视频播放的具体逻辑。协议和具体类的划分允许我们在未来从应用程序中修改具体的播放器实现,而无需改变播放器到应用程序的接口。这可以为应用程序带来极大的灵活性和可维护性。

// VideoPlayerProtocol defines the interface for the video player
protocol VideoPlayerProtocol {
    var url: URL { get set }
    func play()
}

// We implement the VideoPlayerProtocol for AVFoundation
class AVFoundationVideoPlayer: VideoPlayerProtocol {
    var url: URL
    
    init(url: URL) {
        self.url = url
    }
    
    func play() {
        // Play with AVPlayer
    }
}

// We can add other video players using the same VideoPlayerProtocol
class ThirdPartyVideoPlayer: VideoPlayerProtocol {
    var url: URL
    
    init(url: URL) {
        self.url = url
    }
    
    func play() {
        // Play with third-party player SDK
    }
}
4. 纯虚构(Pure Fabrication)

通过PersistenceManagerAuthService两个虚构类将业务逻辑和非业务逻辑分离开来并提高代码的复用性和可维护性。

class AuthService {
  static let shared = AuthService()

  func validate(username: String, password: String, completion: @escaping (Bool) -> Void) {
    // 发送网络请求并验证用户名和密码
    // 请求完成后调用 completion
  }
}

class PersistenceManager {
  static let shared = PersistenceManager()

  func save(user: User) {
    // 保存用户信息
  }

  func load(completion: @escaping (User?) -> Void) {
    // 加载用户信息
    // 加载完成后调用 completion
  }
}

class User {
  var username: String
  var email: String
  var password: String

  func signIn(completion: @escaping (Bool) -> Void) {
    AuthService.shared.validate(username: username, password: password) { isValid in
      completion(isValid)
    }
  }

  func save() {
    PersistenceManager.shared.save(user: self)
  }
}
上一篇 下一篇

猜你喜欢

热点阅读