Избегайте жирных инициализаторов при использовании DI со Swift - PullRequest
0 голосов
/ 17 декабря 2018

Я пытаюсь понять DI в Swift.

Я понимаю, что существуют фреймворки, такие как Swinject и т. Д., Которые могут помочь с DI, однако я очень хочу понять это сам и не использовать магию слишком многих фреймворков.

Возьмите следующеекод в качестве примера .

Инициализатор моего ProfileService будет только расти и становиться все толще и толще, так как этот сервис pseudo расширяется и поскольку проектысодержат более одного класса, один и тот же шаблон будет повторяться много раз.

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

Я думал, возможно, использовать Protocol и Struct для хранения зависимостей ивпрысните это, но не можете понять, как лучше всего это реализовать.

import UIKit

class UserService {
    func currentUser() -> String {
        return "some username"
    }
}

class AvatarService {
    func currentUserAvatarUrl() -> String {
        return "https://foo.bar/image.png"
    }
}

class MessageService {
    func currentInbox() -> [String:String] {
        return [
            "9279n1n2283":"something something",
            "m2j292i2m2n":"something something something"
        ]
    }
}

class ProfileService {
    private let userService: UserService
    private let avatarService: AvatarService
    private let messageService: MessageService

    init(userService: UserService, avatarService: AvatarService, messageService: MessageService) {
        self.userService = userService
        self.avatarService = avatarService
        self.messageService = messageService
    }

    func getLoggedInUser() -> String {
        return userService.currentUser()
    }

    func getUserAvatar() -> String {
        return avatarService.currentUserAvatarUrl()
    }

    func getInboxMessages() -> [String:String] {
        return messageService.currentInbox()
    }
}

let userService = UserService()
let avatarService = AvatarService()
let messageService = MessageService()

let profileService = ProfileService(userService: userService, avatarService: avatarService, messageService: messageService)

profileService.getLoggedInUser()
profileService.getUserAvatar()
profileService.getInboxMessages()

Ответы [ 2 ]

0 голосов
/ 18 декабря 2018

Fat инициализатор, когда вы используете DI, может быть признаком плохого дизайна и показывать, что ваш компонент не простая задача.Таким образом, вы должны попытаться сломать и отделить его.

0 голосов
/ 18 декабря 2018

Моя рекомендация - вообще не делать инъекцию зависимостей в эти классы.Я бы сделал что-то вроде этого:

import Foundation

// MARK: Protocols

protocol UserService {
    var username: String { get }
}

protocol AvatarService {
    var userAvatarURL: URL { get }
}

protocol MessageService {
    var inbox: [String: String] { get }
}

protocol GlobalServiceContext {
    var userService: UserService { get }
    var avatarService: AvatarService { get }
    var messageService: MessageService { get }
}

// MARK: Mock Implementations

class MockUserService: UserService {
    let username = "test_user"
}

class MockAvatarService: AvatarService {
    let userAvatarURL = URL(string: "https://en.wikipedia.org/static/images/project-logos/enwiki.png")!
}

class MockMessageService: MessageService {
    let inbox = [
        "test subject 1": "test message 1",
        "test subject 2": "test message 2",
        "test subject 3": "test message 3",
    ]
}

class MockServiceContext: GlobalServiceContext {
    let userService: UserService = MockUserService()
    let avatarService: AvatarService = MockAvatarService()
    let messageService: MessageService = MockMessageService()
}

// MARK: Mock Implementations

class ProdUserService: UserService {
    // TODO: Substitute real implementation here
    let username = "prod_user"
}

class ProdAvatarService: AvatarService {
    // TODO: Substitute real implementation here
    let userAvatarURL = URL(string: "https://en.wikipedia.org/static/images/project-logos/enwiki.png")!
}

class ProdMessageService: MessageService {
    let inbox = [ // TODO: Substitute real implementation here
        "Prod subject 1": "Prod message 1",
        "Prod subject 2": "Prod message 2",
        "Prod subject 3": "Prod message 3",
    ]
}

class ProdServiceContext: GlobalServiceContext {
    let userService: UserService = ProdUserService()
    let avatarService: AvatarService = ProdAvatarService()
    let messageService: MessageService  = ProdMessageService()
}

// MARK: Usage

let ServiceContext: GlobalServiceContext = MockServiceContext()

class ProfileService {
    var username: String { return ServiceContext.userService.username }
    var userAvatarURL: URL { return ServiceContext.avatarService.userAvatarURL }
    var inbox: [String:String] { return ServiceContext.messageService.inbox }
}

let profileService = ProfileService()
print(profileService.username)
print(profileService.userAvatarURL)
print(profileService.inbox)

Содержит все ваше глобальное состояние (ваши API, сервисы, базы данных и т. Д.):

  • в один объект
  • это доступно глобально
  • , значение которого можно заменить на фиктивную реализацию
...