Last active
December 13, 2022 07:39
-
-
Save alexnikol/068434afbe586d9dd755810acb71eeb3 to your computer and use it in GitHub Desktop.
From procedural code to decorator pattern
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// CODE 1 | |
class HTTPClient { | |
func load(url: URL, completion: @escaping (Response) -> Void) { | |
// call server | |
// complete with result | |
} | |
} | |
// Responsibilities: | |
/// - Send request and return response |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// CODE 10 | |
/// Initialization of controller with dependency injection | |
let vc = ClientViewController(httpClient: HTTPClientFactory.makeHTTPClient()) | |
class ClientViewController: UIViewController { | |
let httpClient: HTTPClientProtocol | |
init(httpClient: HTTPClientProtocol) { | |
self.httpClient = httpClient | |
} | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
let someURL = URL(string: "http://some-url.com")! | |
httpClient.load(url: someURL, completion: { response in | |
// handle result | |
}) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// CODE 2 | |
class HTTPClient { | |
let storage = CredentialsStorage() | |
func load(url: URL, completion: @escaping (Response) -> Void) { | |
if storage.isTokenExpired { | |
refreshToken(token: storage.token, completion: { _ in | |
// call server | |
// complete with response | |
}) | |
} else { | |
// call server | |
// complete with response | |
} | |
} | |
private func refreshToken(token: String, completion: @escaping (Response) -> Void) { | |
// refresh token | |
// complete with new tokens or error | |
} | |
} | |
// Responsibilities: | |
/// - Send request and return response | |
/// - Managing auth token lifetime, credentials storage | |
/// - Refreshing credentials |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// CODE 3 | |
class HTTPClient { | |
let storage = CredentialsStorage() | |
let permanentBlockList = PermanentBlockList() | |
func load(url: URL, completion: @escaping (Response) -> Void) { | |
guard !permanentBlockList.isBlocked else { | |
completion(.failure(NSError(domain: "PERMANENTLY__BLOCKED", code: 418))) | |
return | |
} | |
if storage.isTokenExpired { | |
refreshToken(token: storage.token, completion: { _ in | |
// call server | |
// if statusC0de = 418 blockList.blockCurrentAccout() | |
// complete with response | |
}) | |
} else { | |
// call server | |
// if statusC0de = 418 blockList.blockCurrentAccout() | |
// complete with response | |
} | |
} | |
private func refreshToken(token: String, completion: @escaping (Response) -> Void) { | |
// refresh token | |
// completion with result | |
} | |
} | |
// Responsibilities: | |
/// - Send request and return response | |
/// - Managing auth token lifetime, credentials storage | |
/// - Refreshing credentials | |
/// - Managing blocked users list, blocked users storage | |
/// - Saving current user to list, and block it from any interactions with our production server |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// CODE 4 | |
protocol HTTPClientProtocol { | |
func load(url: URL, completion: @escaping (Response) -> Void) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// CODE 5 | |
class HTTPClient: HTTPClientProtocol { | |
func load(url: URL, completion: @escaping (Response) -> Void) { | |
// refresh token | |
// completion with result | |
} | |
} | |
// Responsibilities: | |
/// - Send request and return response |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// CODE 6 | |
class RefreshCredentialsHTTPClientDecorator: HTTPClientProtocol { | |
private let storage = CredentialsStorage() | |
private let decoratee: HTTPClientProtocol | |
init(decoratee: HTTPClientProtocol) { | |
self.decoratee = decoratee | |
} | |
func load(url: URL, completion: @escaping (Response) -> Void) { | |
if storage.isTokenExpired { | |
refreshToken(token: storage.token, completion: { [weak self] _ in | |
// refresh token | |
self?.decoratee.load(url: url, completion: completion) | |
}) | |
} else { | |
// refresh token | |
decoratee.load(url: url, completion: completion) | |
} | |
} | |
private func refreshToken(token: String, completion: @escaping (Response) -> Void) { | |
// refresh token | |
// completion with result | |
} | |
} | |
// Responsibilities: | |
/// - Managing auth token lifetime, credentials storage | |
/// - Refreshing credentials |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// CODE 7 | |
class PermanentBlockHTTPClientDecorator: HTTPClientProtocol { | |
private let permanentBlockList = PermanentBlockList() | |
private let decoratee: HTTPClientProtocol | |
init(decoratee: HTTPClientProtocol) { | |
self.decoratee = decoratee | |
} | |
func load(url: URL, completion: @escaping (Response) -> Void) { | |
guard !permanentBlockList.isBlocked else { | |
completion(.failure(NSError(domain: "PERMANENTLY__BLOCKED", code: 418))) | |
return | |
} | |
decoratee.load(url: url, completion: { result in | |
// if statusC0de = 418 blockList.blockCurrentAccout() complete with block error | |
// else complete with decoratee's result | |
completion(result) | |
}) | |
} | |
} | |
// Responsibilities: | |
/// - Managing blocked users list, blocked users storage | |
/// - Saving current user to list, and block it from any interactions with our production server |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// CODE 8 | |
class HTTPClientLoggerDecorator: HTTPClientProtocol { | |
private let decoratee: HTTPClientProtocol | |
init(decoratee: HTTPClientProtocol) { | |
self.decoratee = decoratee | |
} | |
func load(url: URL, completion: @escaping (Response) -> Void) { | |
// start measure | |
decoratee.load(url: url) { result in | |
// end measure and save to some storage the results | |
// completion with result | |
} | |
} | |
} | |
// Responsibilities: | |
/// - Measure real network requests to the server |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// CODE 9 | |
//// MARK: - Objects graph: | |
/// PermanentBlockHTTPClientDecorator | |
/// -> RefreshCredentialsHTTPClientDecorator | |
/// -> HTTPClientLoggerDecorator | |
/// -> HTTPClient | |
class HTTPClientFactory { | |
private init() {} | |
static func makeHTTPClient() -> HTTPClientProtocol { | |
let httpClient = HTTPClient() | |
let httpClientLoggerDecorator = HTTPClientLoggerDecorator(decoratee: httpClient) | |
let refreshCredentialsHTTPClientDecorator = RefreshCredentialsHTTPClientDecorator(decoratee: httpClientLoggerDecorator) | |
let permanentBlockHTTPClientDecorator = PermanentBlockHTTPClientDecorator(decoratee: refreshCredentialsHTTPClientDecorator) | |
return permanentBlockHTTPClientDecorator | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment