Irbisa · Шпаргалка

ООП, SOLID и паттерны проектирования

ООП и SOLID в Swift, DRY, KISS и YAGNI, связность и зацепление, внедрение зависимостей, паттерны GoF, delegate, target-action, observer и coordinator, антипаттерны

Middle-разработчик · 18 вопросов

  1. 01

    Что означают DRY, KISS и YAGNI и когда устранение дублирования делает код хуже?

    DRY: у каждого знания есть одно авторитетное место.

  2. 02

    Что такое связанность (coupling) и связность (cohesion) и как они относятся к принципу единственной ответственности?

    Связность (cohesion) показывает, насколько части одного типа или модуля принадлежат друг другу; связанность (coupling) показывает, насколько один тип зависит от деталей другого.

  3. 03

    Как реализовать синглтон в Swift, потокобезопасен ли он и что не так с синглтонами?

    static let shared плюс private init().

    @MainActorfinal class SessionStore {  static let shared = SessionStore()   // created lazily, exactly once, even under contention  private init() {}                    // nobody can create a second one   private(set) var token: String?…
  4. 04

    Как реализовать паттерн делегат в Swift и почему методы делегатов в Cocoa передают отправителя и используют will, did и should?

    Объявите протокол событий, ограниченный классами, дайте делегирующему объекту weak var delegate и вызывайте его в нужные моменты.

    @MainActorprotocol RatingViewDelegate: AnyObject {      // AnyObject, so the reference can be weak  func ratingView(_ view: RatingView, shouldSelect stars: Int) -> Bool   // asks first  func ratingView(_ view: RatingView, didSelect stars: Int)              // reports after}…
  5. 05

    Какой принцип SOLID нарушает CachedFeedLoader и что происходит с вызывающей функцией feed(from:)?

    protocol FeedLoader {  func load(completion: @escaping (Result<[Post], Error>) -> Void)  // contract: exactly once}struct CachedFeedLoader: FeedLoader {  let cache: FeedCache  let remote: FeedLoader  func load(completion: @escaping (Result<[Post], Error>) -> Void) {    completion(.success(cache.posts))     // cached posts at once    remote.load(completion: completion)   // then the fresh ones  }}func feed(from loader: FeedLoader) async throws -> [Post] {  try await withCheckedThrowingContinuation { continuation in    loader.load { continuation.resume(with: $0) }  }}

    Принцип подстановки Лисков: код, работающий с FeedLoader, полагается на его обещание вызвать completion ровно один раз, а CachedFeedLoader вызывает его дважды.

  6. 06

    Как выглядит принцип разделения интерфейсов в Swift и как реализации по умолчанию в протоколе могут скрыть его нарушение?

    Ни один клиент не должен зависеть от методов, которыми не пользуется.

    struct User { let id: String; var name: String } // before: every screen and every test double carries all of itprotocol UserService {  func currentUser() async throws -> User  func updateAvatar(_ jpeg: Data) async throws…
  7. 07

    Чем внедрение зависимостей отличается от инверсии зависимостей и какому модулю должен принадлежать протокол?

    Внедрение зависимостей (dependency injection) описывает технику: объект получает свои зависимости, а не создаёт их сам.

    // Feed module: the policy owns the abstraction and names it in its own termspublic struct Post: Sendable {  public let id: String  public init(id: String) { self.id = id }}…
  8. 08

    Чем Factory Method отличается от Abstract Factory и где каждый из них встречается в iOS-коде?

    Фабричный метод (factory method): один переопределяемый метод, который создаёт объект, так что конкретный тип выбирает наследник или конформер.

    // Factory method: UIView asks its class which layer to create, a subclass decidesfinal class GradientView: UIView {  override class var layerClass: AnyClass { CAGradientLayer.self }  var gradientLayer: CAGradientLayer { layer as! CAGradientLayer }}…
  9. 09

    Нужен ли в Swift паттерн Builder, если есть аргументы по умолчанию и поэлементные инициализаторы?

    Редко в классическом виде: аргументы по умолчанию и поэлементные (memberwise) инициализаторы уже решают проблему «телескопических» конструкторов, ради которой в Java существует билдер.

    // Java needs a builder for optional parameters; Swift has default argumentsstruct Toast {  var message: String  var icon: String? = nil  var duration: Duration = .seconds(2)  var isDismissible = true…
  10. 10

    Чем Adapter отличается от Facade и как обернуть сторонний SDK, чтобы он не просачивался в фичи?

    Адаптер подгоняет один существующий тип под интерфейс, которого ждёт ваш код; фасад ставит один простой интерфейс перед целой подсистемой.

    // The interfaces the app wants, in the app's own termsprotocol SettingsStore {  func bool(forKey key: String) -> Bool  func set(_ value: Bool, forKey key: String)}…
  11. 11

    Что такое паттерн Decorator в Swift, чем он отличается от расширения и наследника и чем от него отличается Proxy?

    Декоратор соответствует тому же протоколу, что и оборачиваемый объект, и добавляет поведение вокруг каждого вызова, поэтому кэширование, логирование или повторы складываются слоями для отдельного экземпляра.

    protocol ImageLoading {  func image(for url: URL) async throws -> UIImage} struct RemoteImageLoader: ImageLoading {  func image(for url: URL) async throws -> UIImage {…
  12. 12

    У делегата только один слушатель. Как оповещать несколько наблюдателей и при этом не держать их живыми?

    Храните наблюдателей в коллекции слабых ссылок, например NSHashTable.weakObjects(), и при изменении проходите по снимку.

    @MainActor protocol PlayerObserver: AnyObject {  func player(_ player: AudioPlayer, didChange state: AudioPlayer.State)} @MainActor final class AudioPlayer {  enum State { case idle, playing, paused }…
  13. 13

    Что такое Chain of Responsibility и как глубоко вложенная ячейка может достучаться до своего view controller через responder chain вместо делегата?

    Запрос идёт по цепочке обработчиков, пока один из них его не обработает, и отправитель так и не узнаёт, какой именно.

    @MainActor protocol CartActionHandling: AnyObject {  func addToCart(productID: String)} extension UIResponder {  // Walks up the chain (superviews, the controller, the window, the app) to the first match…
  14. 14

    Что такое паттерн Strategy, как выразить его в Swift и чем он отличается от паттерна State?

    Стратегия прячет каждый вариант алгоритма за одним интерфейсом, а вызывающий выбирает или меняет вариант во время выполнения, поэтому код, который им пользуется, никогда не делает switch по вариантам.

    protocol ShippingPolicy {  func cost(for cart: Cart) -> Decimal} struct FlatRate: ShippingPolicy {  let amount: Decimal…
  15. 15

    Найдите баг: пользователь нажимает Stop, пока видео ещё буферизуется, и через мгновение оно всё равно начинает играть.

    @MainActor final class PlayerModel {  private(set) var isBuffering = false  private(set) var isPlaying = false  private let engine: PlaybackEngine  init(engine: PlaybackEngine) { self.engine = engine }   func playTapped(_ url: URL) { isBuffering = true; engine.load(url) }  func stopTapped() { isPlaying = false; engine.pause() }   // The engine calls this once enough of the video has arrived  func bufferingDidFinish() {    isBuffering = false    isPlaying = true    engine.start()  }}

    stopTapped() не завершает буферизацию, а bufferingDidFinish() запускает воспроизведение, не проверяя, чего пользователь хочет сейчас.

  16. 16

    Что такое паттерн Command и как UndoManager использует его, чтобы дать приложению отмену и повтор?

    Команда превращает запрос в объект, который можно сохранить, поставить в очередь, записать в лог или откатить позже.

    struct Shape: Identifiable { let id = UUID(); var color: String } @MainActor final class Canvas {  private(set) var shapes: [Shape] = []  let undoManager = UndoManager()…
  17. 17

    Что такое неправильная абстракция, как её распознать и как из неё выйти без переписывания?

    Абстракция неправильна, когда вызывающим приходится передавать флаги или особые случаи, чтобы получить от неё своё поведение.

    // before: one row for every screen, and a flag for every new callerstruct ItemRow: View {  let title: String  var imageURL: URL?  var price: Decimal?  var isProfile = false          // added for the profile screen…
  18. 18

    Какие антипаттерны вы ищете в доставшейся iOS-кодовой базе и как выбраться из god object, синглтонов повсюду и BaseViewController?

    Первым делом я ищу god object, синглтоны как скрытые зависимости и глубокий BaseViewController.