Связывание интерфейсов с конкретными реализациями осуществляется через инъекцию зависимостей (Dependency Injection), фабрики (Factories) и контейнеры зависимостей (Dependency Injection Containers). Эти методы абстрагируют зависимости и упрощают замену реализаций.
Инъекция зависимостей передает зависимости через конструкторы, сеттеры или методы, обеспечивая гибкость и упрощая тестирование.
interface UserRepository {
findById(id: string): Promise<User | null>;
}
class UserRepositoryImpl implements UserRepository {
public async findById(id: string): Promise<User | null> {
return { id, name: "John Doe" };
}
}
class UserService {
private userRepository: UserRepository;
constructor(userRepository: UserRepository) {
this.userRepository = userRepository;
}
public async getUserById(id: string): Promise<User | null> {
return await this.userRepository.findById(id);
}
}
const userRepository = new UserRepositoryImpl();
const userService = new UserService(userRepository);Фабрики создают объекты, инкапсулируя логику создания и облегчая управление.
interface UserRepository {
findById(id: string): Promise<User | null>;
}
class UserRepositoryImpl implements UserRepository {
public async findById(id: string): Promise<User | null> {
return { id, name: "John Doe" };
}
}
class UserRepositoryFactory {
static create(): UserRepository {
return new UserRepositoryImpl();
}
}
const userRepository = UserRepositoryFactory.create();
const userService = new UserService(userRepository);Контейнеры управляют созданием и разрешением зависимостей централизованно.
import "reflect-metadata";
import { Container, injectable, inject } from "inversify";
interface UserRepository {
findById(id: string): Promise<User | null>;
}
@injectable()
class UserRepositoryImpl implements UserRepository {
public async findById(id: string): Promise<User | null> {
return { id, name: "John Doe" };
}
}
@injectable()
class UserService {
private userRepository: UserRepository;
constructor(@inject("UserRepository") userRepository: UserRepository) {
this.userRepository = userRepository;
}
public async getUserById(id: string): Promise<User | null> {
return await this.userRepository.findById(id);
}
}
const container = new Container();
container.bind<UserRepository>("UserRepository").to(UserRepositoryImpl);
container.bind<UserService>(UserService).toSelf();
const userService = container.get<UserService>(UserService);
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3😁1
Singletone нарушает несколько принципов хорошего проектирования кода, особенно из набора принципов SOLID.
Класс должен иметь только одну причину для изменения. Singletone выполняет две задачи: управляет своим состоянием и контролирует создание своего единственного экземпляра. Это смешивание обязанностей делает класс сложнее и приводит к ситуации, когда изменение в одной из обязанностей может повлиять на другую.
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
return cls._instance
def some_business_logic(self):
pass
# Здесь Singleton отвечает за управление состоянием и контролем создания экземпляра
Классы должны быть открыты для расширения, но закрыты для модификации. Singletone трудно расширять без модификации его кода. Для изменения логики создания экземпляра или поведения необходимо изменять сам класс, что нарушает OCP.
Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба типа модулей должны зависеть от абстракций. Singletone нарушает DIP, так как привязывает код к конкретной реализации через глобальный доступ к своему экземпляру. Это делает тестирование и замену реализации сложными, поскольку зависимость жестко закодирована.
class DatabaseConnection:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(DatabaseConnection, cls).__new__(cls, *args, **kwargs)
return cls._instance
# Любой код, использующий DatabaseConnection, теперь жестко привязан к этой реализации
Может создавать скрытые зависимости: Singletone часто используют глобальные состояния, которые могут усложнить тестирование. Сложность при мокировании: Из-за глобального доступа к синглтону сложно подменить его поведение в тестах.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Полиморфизм широко используется в iOS разработке для создания гибкого и расширяемого кода.
Протоколы в Swift позволяют определять методы и свойства, которые классы, структуры или перечисления должны реализовать. Это позволяет создавать полиморфные интерфейсы, которые могут быть реализованы различными типами.
protocol Drawable {
func draw()
}
class Circle: Drawable {
func draw() {
print("Drawing a circle")
}
}
class Square: Drawable {
func draw() {
print("Drawing a square")
}
}
func renderShape(_ shape: Drawable) {
shape.draw()
}
let shapes: [Drawable] = [Circle(), Square()]
for shape in shapes {
renderShape(shape) // Полиморфный вызов метода draw()
}Классы могут наследовать методы и свойства от родительских классов, что позволяет использовать полиморфизм через переопределение методов.
class Animal {
func makeSound() {
print("Some generic animal sound")
}
}
class Dog: Animal {
override func makeSound() {
print("Bark")
}
}
class Cat: Animal {
override func makeSound() {
print("Meow")
}
}
let animals: [Animal] = [Dog(), Cat()]
for animal in animals {
animal.makeSound() // Полиморфный вызов метода makeSound()
}Обобщения позволяют писать универсальный код, который работает с любыми типами, соответствующими определенным требованиям.
func printArray<T>(items: [T]) {
for item in items {
print(item)
}
}
printArray(items: [1, 2, 3])
printArray(items: ["a", "b", "c"])UIKit и SwiftUI активно используют полиморфизм. Например,
UIView в UIKit и View в SwiftUI являются базовыми классами и протоколами, которые реализуют множество различных типов пользовательских интерфейсов.let views: [UIView] = [UILabel(), UIButton(), UIImageView()]
for view in views {
// Полиморфное использование метода addSubview()
someParentView.addSubview(view)
}
Полиморфизм используется в сочетании с Dependency Injection для предоставления различных реализаций интерфейсов или протоколов в зависимости от контекста.
protocol DataService {
func fetchData() -> String
}
class APIService: DataService {
func fetchData() -> String {
return "Data from API"
}
}
class MockService: DataService {
func fetchData() -> String {
return "Mock data"
}
}
class DataManager {
private var service: DataService
init(service: DataService) {
self.service = service
}
func getData() -> String {
return service.fetchData()
}
}
let apiManager = DataManager(service: APIService())
let mockManager = DataManager(service: MockService())
print(apiManager.getData()) // "Data from API"
print(mockManager.getData()) // "Mock data"Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1
Протоколы в Swift позволяют определять методы и свойства, которые классы, структуры или перечисления должны реализовать. Они обеспечивают гибкость и полиморфизм, позволяя различным типам обрабатываться одинаковым образом.
Определение протокола
protocol Drawable {
func draw()
}Реализация протокола в классе
class Circle: Drawable {
func draw() {
print("Drawing a circle")
}
}
class Square: Drawable {
func draw() {
print("Drawing a square")
}
}Использование протоколов
let shapes: [Drawable] = [Circle(), Square()]
for shape in shapes {
shape.draw()
}
// Вывод:
// Drawing a circle
// Drawing a square
Протоколы можно расширять, добавляя методы с реализацией:
extension Drawable {
func description() -> String {
return "This is a drawable object."
}
}
class Triangle: Drawable {
func draw() {
print("Drawing a triangle")
}
}
let triangle = Triangle()
print(triangle.description()) // "This is a drawable object."Протоколы часто используются для делегирования:
protocol ButtonDelegate: AnyObject {
func didTapButton()
}
class Button {
weak var delegate: ButtonDelegate?
func tap() {
delegate?.didTapButton()
}
}
class ViewController: ButtonDelegate {
func didTapButton() {
print("Button was tapped")
}
}
let button = Button()
let viewController = ViewController()
button.delegate = viewController
button.tap() // "Button was tapped"UITableViewDataSource и UITableViewDelegate
class MyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "Row \(indexPath.row)"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Selected row \(indexPath.row)")
}
}Custom Protocols
protocol PaymentProcessor {
func processPayment(amount: Double)
}
class PayPalProcessor: PaymentProcessor {
func processPayment(amount: Double) {
print("Processing payment of \(amount) via PayPal")
}
}
class StripeProcessor: PaymentProcessor {
func processPayment(amount: Double) {
print("Processing payment of \(amount) via Stripe")
}
}
let processors: [PaymentProcessor] = [PayPalProcessor(), StripeProcessor()]
for processor in processors {
processor.processPayment(amount: 100.0)
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Структуры данных используются для эффективного хранения и управления данными в различных задачах.
Хранение элементов одного типа с доступом по индексу. Список товаров в корзине.
let shoppingCart = ["Apple", "Banana", "Orange"]
Хранение пар ключ-значение. Информация о пользователях по ID.
var userInfo = ["userID1": "John Doe"]
Хранение уникальных элементов. Уникальные теги постов.
var uniqueTags: Set = ["swift", "programming"]
Управление данными в стиле LIFO. Отмена последнего действия.
struct Stack<T> {
private var elements: [T] = []
mutating func push(_ element: T) { elements.append(element) }
mutating func pop() -> T? { elements.popLast() }
} Управление данными в стиле FIFO. Обработка запросов.
struct Queue<T> {
private var elements: [T] = []
mutating func enqueue(_ element: T) { elements.append(element) }
mutating func dequeue() -> T? { elements.isEmpty ? nil : elements.removeFirst() }
} Эффективное добавление и удаление элементов. Управление заказами.
class Node<T> { var value: T; var next: Node?; init(value: T) { self.value = value } } Иерархическая структура данных. Файловая система.
class TreeNode<T> { var value: T; var children: [TreeNode] = [] } Моделирование сетей и связей. Социальные сети.
class Graph { var adjList: [String: [String]] = [:] } Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
private (доступ только внутри текущего файла или типа),
fileprivate (доступ в пределах файла),
internal (доступ по умолчанию в модуле),
public (доступ в любом модуле),
open (доступ для наследования и изменения вне модуля).
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Хранение уникальных элементов. Неупорядоченная коллекция. Быстрый поиск элементов.
var uniqueTags: Set = ["swift", "ios", "programming"]
Хранение пар ключ-значение. Неупорядоченная коллекция с ассоциативным доступом. Быстрый доступ к значениям по ключам.
var userInfo: [String: String] = ["userID1": "John Doe", "userID2": "Jane Smith"]
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Это архитектурные паттерны, используемые в разработке программного обеспечения для разделения логики и представления данных.
Компоненты
Model: Управляет данными и бизнес-логикой.
View: Отвечает за отображение данных и пользовательский интерфейс.
Presenter: Посредник между Model и View. Получает данные из Model и обновляет View.
Связь
View знает о Presenter и взаимодействует с ним.
Presenter знает о View и Model, взаимодействует с обоими.
protocol View: AnyObject {
func updateUI()
}
protocol Presenter {
func loadData()
}
class Model {
var data: String = "Hello, MVP"
}
class MyPresenter: Presenter {
private weak var view: View?
private var model: Model
init(view: View, model: Model) {
self.view = view
self.model = model
}
func loadData() {
// Получение данных из модели
view?.updateUI()
}
}
class MyViewController: UIViewController, View {
var presenter: Presenter!
override func viewDidLoad() {
super.viewDidLoad()
presenter.loadData()
}
func updateUI() {
// Обновление пользовательского интерфейса
}
}Компоненты
Model: Управляет данными и бизнес-логикой.
View: Отвечает за отображение данных и пользовательский интерфейс.
ViewModel: Абстракция, связывающая Model и View. Формирует данные для View и обрабатывает действия от View.
Связь
ViewModel знает о Model и может наблюдать за изменениями в ней. ViewModel предоставляет данные и команды для View. View знает только о ViewModel и не знает о Model напрямую.
class Model {
var data: String = "Hello, MVM"
}
class ViewModel {
private var model: Model
var displayData: String {
return model.data
}
init(model: Model) {
self.model = model
}
func updateModelData(newData: String) {
model.data = newData
}
}
class MyViewController: UIViewController {
var viewModel: ViewModel!
override func viewDidLoad() {
super.viewDidLoad()
// Обновление пользовательского интерфейса с использованием viewModel
print(viewModel.displayData)
}
}MVP: View взаимодействует с Presenter, а Presenter взаимодействует с View и Model.
MVM: View взаимодействует только с ViewModel, а ViewModel взаимодействует с Model.
MVP: View и Presenter имеют двустороннюю зависимость, что может усложнить управление зависимостями.
MVM: ViewModel не зависит от View, что улучшает тестируемость и упрощает архитектуру.
MVP: Тестирование Presenter может быть сложным из-за зависимости от View.
MVM: ViewModel легко тестируется независимо от View.
MVP: Чаще используется в Android-разработке.
MVM: Популярен в iOS-разработке и при использовании SwiftUI.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3👍1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Выбор архитектурного паттерна для разработки приложения зависит от множества факторов, таких как специфика проекта, опыт команды и особенности используемой платформы.
MVP разделяет логику интерфейса пользователя и бизнес-логику, что делает код более модульным и управляемым.
Легче тестировать Presenter отдельно от View, что улучшает покрытие тестами и упрощает отладку.
Presenter управляет всей логикой взаимодействия с View, что делает структуру кода понятной и предсказуемой.
MVP хорошо подходит для проектов с сложной логикой UI, где требуется много взаимодействий между пользовательским интерфейсом и бизнес-логикой.
ViewModel не знает о View, что уменьшает связанность и улучшает тестируемость.
MVVM особенно хорошо работает с фреймворками, поддерживающими привязки данных, такими как SwiftUI или RxSwift. Это упрощает синхронизацию данных между View и ViewModel.
ViewModel формирует данные для View, что упрощает логику представления и делает код более чистым.
ViewModel легко тестируется, поскольку не зависит от конкретной реализации View, что улучшает модульное тестирование.
Использование MVVM в SwiftUI позволяет легко управлять состоянием приложения и автоматически обновлять интерфейс при изменении данных.
MVVM отлично сочетается с реактивным программированием, предоставляя мощные возможности для управления асинхронными операциями.
import SwiftUI
class Model {
var data: String = "Hello, MVVM"
}
class ViewModel: ObservableObject {
@Published var displayData: String = ""
private var model: Model
init(model: Model) {
self.model = model
self.displayData = model.data
}
func updateData(newData: String) {
model.data = newData
self.displayData = newData
}
}
struct ContentView: View {
@ObservedObject var viewModel: ViewModel
var body: some View {
Text(viewModel.displayData)
}
}
MVP используется для четкого разделения логики представления и бизнес-логики в сложных проектах на UIKit.
protocol View: AnyObject {
func updateUI(with data: String)
}
class Model {
var data: String = "Hello, MVP"
}
class Presenter {
private weak var view: View?
private var model: Model
init(view: View, model: Model) {
self.view = view
self.model = model
}
func loadData() {
view?.updateUI(with: model.data)
}
}
class ViewController: UIViewController, View {
private var presenter: Presenter!
override func viewDidLoad() {
super.viewDidLoad()
presenter = Presenter(view: self, model: Model())
presenter.loadData()
}
func updateUI(with data: String) {
// Обновление интерфейса
print(data)
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
В программировании существуют различные типы ссылок, которые определяют, как объекты управляются и удерживаются в памяти.
По умолчанию в Swift все ссылки являются сильными. Сильная ссылка удерживает объект в памяти до тех пор, пока существует хотя бы одна сильная ссылка на него. Для стандартного владения объектами.
class Person {
var name: String
init(name: String) {
self.name = name
}
}
var person1: Person? = Person(name: "Alice")
var person2 = person1 // person2 также является сильной ссылкой на тот же объект Слабая ссылка не удерживает объект в памяти. Если все сильные ссылки на объект удаляются, объект освобождается, и слабая ссылка автоматически становится nil. Для предотвращения циклов удержания, особенно в делегатах и замыканиях.
class Person {
var name: String
init(name: String) {
self.name = name
}
}
class Apartment {
weak var tenant: Person?
}
var alice: Person? = Person(name: "Alice")
var apt = Apartment()
apt.tenant = alice // Слабая ссылка на alice Неактивная ссылка, как и слабая, не удерживает объект в памяти, но в отличие от слабой ссылки, она не может быть nil. Используется, когда объект гарантированно существует на протяжении всего жизненного цикла. Для циклических зависимостей, где одна сторона всегда будет существовать.
class Person {
var name: String
init(name: String) {
self.name = name
}
}
class Apartment {
unowned var tenant: Person
init(tenant: Person) {
self.tenant = tenant
}
}
var alice: Person? = Person(name: "Alice")
var apt = Apartment(tenant: alice!) // Неактивная ссылка на alice Предоставляют прямой доступ к объекту без автоматического управления памятью. Используется редко и требует ручного управления памятью. Для специфических задач, где необходим контроль над управлением памятью.
import Foundation
let unmanagedReference = Unmanaged.passRetained(NSObject())
let retainedObject = unmanagedReference.takeRetainedValue()
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3👍1
В iOS-разработке существует несколько способов создания и управления макетами (layouts) пользовательского интерфейса.
Визуальный редактор, встроенный в Xcode, который позволяет разрабатывать пользовательский интерфейс с помощью перетаскивания элементов. Удобный графический интерфейс, быстрый просмотр изменений, поддержка Auto Layout. Меньшая гибкость по сравнению с программным кодом. Создание и настройка интерфейса с помощью storyboard или xib файлов.
Система ограничений (constraints), которая позволяет определять правила расположения и размеров элементов интерфейса. Адаптивные интерфейсы для различных устройств и ориентаций, мощные инструменты для сложных макетов. Может быть сложно освоить, особенно для сложных макетов.
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
Создание и настройка элементов интерфейса полностью через код, без использования Interface Builder. Полный контроль над процессом создания интерфейса, легко управлять версиями. Требует больше времени и усилий для настройки, особенно для сложных макетов.
let label = UILabel(frame: CGRect(x: 50, y: 50, width: 200, height: 50))
label.text = "Hello, World!"
view.addSubview(label)
Компоненты интерфейса, которые автоматически располагают и масштабируют свои подвиды (subviews) в вертикальном или горизонтальном направлении. Упрощает создание и управление сложными макетами, поддержка Auto Layout.: Ограниченная гибкость для некоторых типов макетов.
let stackView = UIStackView(arrangedSubviews: [label, button])
stackView.axis = .vertical
stackView.spacing = 10
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
NSLayoutConstraint.activate([
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
Новый декларативный фреймворк для создания пользовательских интерфейсов, представленный Apple в 2019 году. Декларативный синтаксис, простой в использовании, интеграция с Swift, поддержка различных платформ (iOS, macOS, watchOS, tvOS). Требует iOS 13 и выше, меньшая поддержка старых устройств.
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Text("Hello, World!")
Button(action: {
print("Button tapped")
}) {
Text("Tap me")
}
}
}
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1