mobile-best-practices 1.1.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/data/design-patterns.csv +31 -0
- package/assets/data/platforms/android.csv +158 -0
- package/assets/scripts/__pycache__/core.cpython-314.pyc +0 -0
- package/assets/scripts/core.py +60 -37
- package/assets/scripts/search.py +30 -4
- package/assets/skills/all.md +34 -34
- package/assets/templates/base/skill-content.md +40 -19
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +8 -11
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/versions.d.ts.map +1 -1
- package/dist/commands/versions.js +19 -0
- package/dist/commands/versions.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +86 -29
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
Name,Category,Platform,Keywords,Intent,Code Smell,When To Use,Structure,Bad Example,Good Example,Notes,Reference URL
|
|
2
|
+
Factory Method,Creational,Android,factory method creation switch when type instantiation polymorphism,Define an interface for creating objects letting subclasses decide which class to instantiate,Large when/switch block that creates different objects based on type string or enum,Creating platform-specific views | Creating different API request objects | ViewModel creation based on screen type,Creator → ConcreteCreator → Product,"when (type) {\n ""list"" -> ListScreen()\n ""grid"" -> GridScreen()\n ""detail"" -> DetailScreen()\n}","interface ScreenFactory {\n fun create(): Screen\n}\nclass ListScreenFactory : ScreenFactory {\n override fun create() = ListScreen()\n}",In Android use Hilt @Binds with map multibinding for ViewModel factories. Compose: use CompositionLocal for factory injection.,https://refactoring.guru/design-patterns/factory-method
|
|
3
|
+
Factory Method,Creational,iOS,factory method creation switch pattern instantiation polymorphism swift,Define an interface for creating objects letting subclasses decide which class to instantiate,Large switch statement creating different objects based on type,Creating different view controllers | Building platform-specific UI components | Service instantiation,Protocol → ConcreteFactory → Product,"switch type {\ncase .list: return ListVC()\ncase .grid: return GridVC()\ncase .detail: return DetailVC()\n}","protocol ScreenFactory {\n func create() -> UIViewController\n}\nclass ListScreenFactory: ScreenFactory {\n func create() -> UIViewController { ListVC() }\n}",In SwiftUI use @EnvironmentObject or dependency container for factory injection.,https://refactoring.guru/design-patterns/factory-method
|
|
4
|
+
Factory Method,Creational,Flutter,factory method creation switch pattern instantiation dart widget,Define a factory constructor for creating objects based on type,Large switch creating different widgets based on type string,Creating platform-adaptive widgets | Building different page layouts | Service creation based on config,Factory constructor → Product,"switch (type) {\n case 'list': return ListPage();\n case 'grid': return GridPage();\n}","abstract class PageFactory {\n Widget create();\n}\nclass ListPageFactory implements PageFactory {\n @override\n Widget create() => const ListPage();\n}",Dart has built-in factory constructors. Use them with abstract classes for clean factory patterns.,https://refactoring.guru/design-patterns/factory-method
|
|
5
|
+
Abstract Factory,Creational,All,abstract factory family platform theme component creation,Provide an interface for creating families of related objects without specifying concrete classes,Platform-specific UI components scattered across codebase with duplicated if-platform checks,Creating themed UI component sets | Platform-specific service implementations | White-label app variants,AbstractFactory → ConcreteFactory → AbstractProduct → ConcreteProduct,"if (platform == ""ios"") {\n button = IOSButton()\n dialog = IOSDialog()\n} else {\n button = AndroidButton()\n dialog = AndroidDialog()\n}","interface UIFactory {\n fun createButton(): Button\n fun createDialog(): Dialog\n}\nclass MaterialFactory : UIFactory { ... }\nclass CupertinoFactory : UIFactory { ... }",In Flutter use Platform.isIOS with factory. In Compose use MaterialTheme as a built-in abstract factory for theming.,https://refactoring.guru/design-patterns/abstract-factory
|
|
6
|
+
Builder,Creational,Android,builder telescoping constructor optional parameters configuration,Construct complex objects step by step allowing different representations,Constructor with many optional parameters | Telescoping constructors | Multiple overloaded constructors,Building complex request objects | Configuring notification builders | Multi-step form data assembly,Builder → ConcreteBuilder → Director → Product,"class Config(\n val url: String,\n val timeout: Int = 30,\n val retries: Int = 3,\n val cache: Boolean = true,\n val headers: Map<String,String> = emptyMap()\n) // 10+ params","class Config private constructor(builder: Builder) {\n class Builder {\n private var url = """"\n fun url(v: String) = apply { url = v }\n fun build() = Config(this)\n }\n}",Kotlin data classes with default params often eliminate the need for Builder. Use Builder only when construction has validation or ordering constraints.,https://refactoring.guru/design-patterns/builder
|
|
7
|
+
Builder,Creational,iOS,builder telescoping constructor optional parameters swift configuration,Construct complex objects step by step allowing different representations,Init with many optional parameters | Long initializer parameter lists,Building complex request configurations | Constructing attributed strings | Multi-step object assembly,Builder → Product,"init(url: String, timeout: Int = 30, retries: Int = 3, cache: Bool = true, headers: [String:String] = [:])","class ConfigBuilder {\n private var url = """"\n func url(_ v: String) -> Self { url = v; return self }\n func build() -> Config { Config(url: url) }\n}",Swift @resultBuilder is a powerful alternative for DSL-style builders. Use for complex SwiftUI view composition.,https://refactoring.guru/design-patterns/builder
|
|
8
|
+
Singleton,Creational,Android,singleton global state shared instance single object,Ensure a class has only one instance and provide global access to it,Multiple instances of a manager class causing state inconsistencies | Global mutable state accessed everywhere,Database instance | Shared preferences wrapper | Analytics tracker | Network client,Singleton → Instance,"class DbHelper {\n companion object {\n var instance: DbHelper? = null\n fun get(): DbHelper {\n if (instance == null) instance = DbHelper()\n return instance!!\n }\n }\n}","@Singleton\nclass DbHelper @Inject constructor(\n private val db: AppDatabase\n) { ... }",Avoid manual singletons in Android. Use Hilt @Singleton scope instead. Manual singletons leak context and prevent testing.,https://refactoring.guru/design-patterns/singleton
|
|
9
|
+
Singleton,Creational,Flutter,singleton global state shared instance dart single,Ensure a class has only one instance and provide global access to it,Multiple instances causing duplicated state | Global variables scattered across files,Service locators | Shared configuration | Event bus,Singleton → Instance,"class ApiClient {\n static ApiClient? _instance;\n factory ApiClient() {\n _instance ??= ApiClient._();\n return _instance!;\n }\n ApiClient._();\n}","// Use get_it or riverpod instead\nfinal getIt = GetIt.instance;\ngetIt.registerSingleton<ApiClient>(ApiClient());",Prefer get_it or riverpod for DI over manual singletons. Manual singletons make testing and hot reload harder in Flutter.,https://refactoring.guru/design-patterns/singleton
|
|
10
|
+
Adapter,Structural,Android,adapter wrapper interface incompatible api response mapping convert,Convert the interface of a class into another interface clients expect,Manual mapping code duplicated across multiple call sites | Incompatible third-party API responses,Mapping API DTOs to domain models | Wrapping legacy code | Converting between library types,Target → Adapter → Adaptee,"// Scattered everywhere\nval user = User(\n name = response.full_name,\n email = response.email_address,\n avatar = response.profile_image_url\n)","class UserMapper {\n fun toDomain(dto: UserDto): User = User(\n name = dto.full_name,\n email = dto.email_address,\n avatar = dto.profile_image_url\n )\n}",Essential for Clean Architecture. Place mappers at layer boundaries. In Kotlin use extension functions for concise mapping.,https://refactoring.guru/design-patterns/adapter
|
|
11
|
+
Adapter,Structural,Flutter,adapter wrapper interface incompatible api response mapping dart convert,Convert the interface of a class into another interface clients expect,Manual fromJson/toJson scattered | Incompatible plugin API responses,Mapping API responses to models | Wrapping platform channels | Converting between packages,Target → Adapter → Adaptee,"final user = User(\n name: json['full_name'],\n email: json['email_address'],\n);","class UserMapper {\n User toDomain(UserDto dto) => User(\n name: dto.fullName,\n email: dto.emailAddress,\n );\n}",Use freezed with json_serializable for automatic DTO-to-model mapping. Place mappers in data layer.,https://refactoring.guru/design-patterns/adapter
|
|
12
|
+
Decorator,Structural,All,decorator wrapper extend behavior dynamically composition inheritance,Attach additional responsibilities to objects dynamically without modifying their code,Subclass explosion to add feature variations | Copy-paste code for cross-cutting concerns,Adding logging to repository calls | Caching layer around data source | Adding analytics to use cases,Component → Decorator → ConcreteDecorator,"class CachingUserRepo : UserRepository { ... }\nclass LoggingUserRepo : UserRepository { ... }\nclass CachingLoggingUserRepo : UserRepository { ... } // explosion","class LoggingDecorator(private val inner: UserRepository) : UserRepository {\n override suspend fun getUser(id: String): User {\n log(""getUser called"")\n return inner.getUser(id)\n }\n}",Compose decorators via DI. In Hilt use @Binds with qualifiers. In Flutter chain providers. Keeps each concern in a single class.,https://refactoring.guru/design-patterns/decorator
|
|
13
|
+
Facade,Structural,All,facade simplify subsystem interface complex api,Provide a simplified interface to a complex subsystem,Client code directly calls multiple subsystem classes | Long setup sequences repeated,Wrapping complex SDK initialization | Simplifying multi-step operations | Providing clean API for feature modules,Facade → Subsystem classes,"// In ViewModel\nval auth = FirebaseAuth.getInstance()\nval db = FirebaseFirestore.getInstance()\nval storage = FirebaseStorage.getInstance()\nauth.signIn(email, pass).then { db.collection(""users"").doc(it.uid).set(data) }","class AuthFacade @Inject constructor(\n private val auth: FirebaseAuth,\n private val db: Firestore,\n private val storage: Storage\n) {\n suspend fun signUp(email: String, pass: String, profile: Profile) { ... }\n}",Common in Android for Firebase/SDK wrappers. The Repository pattern is essentially a Facade over data sources.,https://refactoring.guru/design-patterns/facade
|
|
14
|
+
Composite,Structural,All,composite tree recursive hierarchy nested structure,Compose objects into tree structures to represent part-whole hierarchies,Recursive if/else to handle nested structures | Type-checking for leaf vs branch nodes,Building nested navigation menus | File/folder structures | Comment threads with replies | UI component trees,Component → Leaf → Composite,"fun render(item: Any) {\n if (item is File) renderFile(item)\n else if (item is Folder) {\n renderFolder(item)\n item.children.forEach { render(it) }\n }\n}","sealed interface FileNode {\n fun render(): Composable\n}\nclass File : FileNode { override fun render() = ... }\nclass Folder(val children: List<FileNode>) : FileNode {\n override fun render() { children.forEach { it.render() } }\n}",Kotlin sealed interfaces are ideal for Composite. In Flutter use widget composition naturally. In SwiftUI use ViewBuilder.,https://refactoring.guru/design-patterns/composite
|
|
15
|
+
Proxy,Structural,All,proxy lazy loading caching access control wrapper,Provide a surrogate or placeholder for another object to control access to it,Direct access to expensive resources without caching | Missing access control on sensitive operations,Lazy-loading images | Caching API responses | Access control for premium features | Logging method calls,Subject → Proxy → RealSubject,"// Every call hits network\nsuspend fun getUser(id: String): User {\n return api.fetchUser(id)\n}","class CachingUserProxy(private val api: UserApi) : UserRepository {\n private val cache = mutableMapOf<String, User>()\n override suspend fun getUser(id: String): User {\n return cache.getOrPut(id) { api.fetchUser(id) }\n }\n}",In Android Coil/Glide are image loading proxies. OkHttp interceptors act as proxies. Room is a proxy over SQLite.,https://refactoring.guru/design-patterns/proxy
|
|
16
|
+
Observer,Behavioral,Android,observer event listener callback reactive flow state notification,Define a subscription mechanism to notify multiple objects about events,Manual callback interfaces with register/unregister boilerplate | Tight coupling between event producer and consumers,StateFlow/SharedFlow for UI state | LiveData observation | Event bus replacement | Reactive data streams,Subject → Observer,"interface OnDataChanged {\n fun onChanged(data: Data)\n}\nclass Repo {\n private val listeners = mutableListOf<OnDataChanged>()\n fun addListener(l: OnDataChanged) { listeners.add(l) }\n fun notify() { listeners.forEach { it.onChanged(data) } }\n}","class Repo {\n private val _data = MutableStateFlow<Data>(initial)\n val data: StateFlow<Data> = _data.asStateFlow()\n}\n// In Composable\nval data by repo.data.collectAsStateWithLifecycle()",Kotlin Flow/StateFlow is the modern Observer in Android. Avoid manual listener patterns. Use SharedFlow for events and StateFlow for state.,https://developer.android.com/kotlin/flow
|
|
17
|
+
Observer,Behavioral,iOS,observer event listener callback reactive combine notification swift,Define a subscription mechanism to notify multiple objects about events,Manual delegate/callback chains | NotificationCenter overuse,Combine publishers for data streams | @Published property wrappers | AsyncSequence observation,Subject → Observer,"protocol DataDelegate: AnyObject {\n func onDataChanged(_ data: Data)\n}\nclass Repo {\n weak var delegate: DataDelegate?\n func notify() { delegate?.onDataChanged(data) }\n}","class Repo: ObservableObject {\n @Published var data: Data = .initial\n}\n// In SwiftUI View\n@ObservedObject var repo: Repo",SwiftUI @Published + @ObservedObject is the idiomatic Observer. Use Combine for complex reactive chains. Prefer over NotificationCenter.,https://developer.apple.com/documentation/combine
|
|
18
|
+
Observer,Behavioral,Flutter,observer event listener callback reactive stream bloc notification dart,Define a subscription mechanism to notify multiple objects about events,Manual callback functions passed through widget tree | Scattered setState calls,BLoC streams | StreamBuilder | ValueNotifier | ChangeNotifier,Subject → Observer,"class Repo {\n Function(Data)? onChanged;\n void notify(Data data) {\n onChanged?.call(data);\n }\n}","class Repo extends ChangeNotifier {\n Data _data = Data.initial();\n Data get data => _data;\n void update(Data d) { _data = d; notifyListeners(); }\n}\n// Widget: Consumer<Repo>(builder: (_, repo, __) => Text(repo.data))",Flutter has built-in Observer via ChangeNotifier + Provider or BLoC streams. Avoid manual callback passing.,https://docs.flutter.dev/data-and-backend/state-mgmt
|
|
19
|
+
Strategy,Behavioral,All,strategy algorithm if else switch interchangeable behavior policy,Define a family of algorithms and make them interchangeable,Long if/else or when/switch chains selecting different algorithms | Duplicated conditional logic,Choosing sort/filter algorithms | Payment method processing | Authentication strategies | Image compression,Context → Strategy → ConcreteStrategy,"fun sort(items: List<Item>, type: String): List<Item> {\n return when (type) {\n ""price"" -> items.sortedBy { it.price }\n ""name"" -> items.sortedBy { it.name }\n ""date"" -> items.sortedBy { it.date }\n ""rating"" -> items.sortedByDescending { it.rating }\n else -> items\n }\n}","fun interface SortStrategy {\n fun sort(items: List<Item>): List<Item>\n}\nval byPrice = SortStrategy { it.sortedBy { i -> i.price } }\nval byName = SortStrategy { it.sortedBy { i -> i.name } }\n// Usage: strategy.sort(items)",Kotlin fun interfaces and lambdas make Strategy lightweight. In Flutter use typedef. In Swift use closures or protocols.,https://refactoring.guru/design-patterns/strategy
|
|
20
|
+
Command,Behavioral,All,command undo redo action queue execute encapsulate request,Encapsulate a request as an object letting you parameterize queue and undo operations,Undo/redo logic scattered across UI | Action handling mixed into view layer | No operation history,Text editor undo/redo | Drawing app operations | Form step navigation | Offline action queue,Command → ConcreteCommand → Invoker → Receiver,"// In ViewModel mixed with UI logic\nfun onBold() { text = text.bold(); history.add(""bold"") }\nfun onUndo() {\n when (history.last()) {\n ""bold"" -> text = text.unbold()\n ""italic"" -> text = text.unitalic()\n }\n}","interface Command {\n fun execute()\n fun undo()\n}\nclass BoldCommand(private val editor: Editor) : Command {\n override fun execute() { editor.bold() }\n override fun undo() { editor.unbold() }\n}\nclass History {\n private val stack = ArrayDeque<Command>()\n fun execute(cmd: Command) { cmd.execute(); stack.addLast(cmd) }\n fun undo() { stack.removeLastOrNull()?.undo() }\n}",Essential for apps with undo/redo. Combine with MVI for event sourcing. Useful for offline-first apps queuing mutations.,https://refactoring.guru/design-patterns/command
|
|
21
|
+
State,Behavioral,All,state machine transition if else status enum behavior change,Allow an object to alter its behavior when its internal state changes,Complex if/else chains checking current state | Scattered state transition logic | Boolean flags controlling behavior,Order status workflows | Player states (play/pause/stop) | Authentication flow | Download manager states,Context → State → ConcreteState,"fun handle(order: Order) {\n if (order.status == ""pending"") {\n if (canProcess(order)) { order.status = ""processing"" }\n } else if (order.status == ""processing"") {\n if (isShipped(order)) { order.status = ""shipped"" }\n } else if (order.status == ""shipped"") { ... }\n}","sealed class OrderState {\n abstract fun next(order: Order): OrderState\n object Pending : OrderState() {\n override fun next(order: Order) = if (canProcess(order)) Processing else this\n }\n object Processing : OrderState() {\n override fun next(order: Order) = if (isShipped(order)) Shipped else this\n }\n}",Kotlin sealed classes are perfect for State pattern. Combine with MVI for predictable state machines. In Flutter use bloc states.,https://refactoring.guru/design-patterns/state
|
|
22
|
+
Template Method,Behavioral,All,template method algorithm skeleton hook override base class,Define the skeleton of an algorithm deferring some steps to subclasses,Duplicated algorithm with minor variations across classes | Copy-paste code with small differences,Base analytics tracker with platform variants | Common data sync flow | Standardized screen lifecycle,AbstractClass → ConcreteClass,"class AndroidSync {\n fun sync() { fetchLocal(); fetchRemote(); merge(); saveLocal(); }\n}\nclass IOSSync {\n fun sync() { fetchLocal(); fetchRemote(); merge(); saveLocal(); }\n} // duplicated","abstract class DataSync {\n fun sync() { fetchLocal(); fetchRemote(); merge(); saveLocal() }\n abstract fun fetchLocal(): Data\n abstract fun fetchRemote(): Data\n protected open fun merge(): Data { /* default merge */ }\n abstract fun saveLocal(data: Data)\n}",In Kotlin prefer composition over inheritance when possible. Use Template Method when the algorithm skeleton is truly fixed.,https://refactoring.guru/design-patterns/template-method
|
|
23
|
+
Chain of Responsibility,Behavioral,All,chain responsibility handler request pipeline interceptor middleware,Pass requests along a chain of handlers letting each decide to process or pass along,Nested if/else for request validation/processing | Long validation sequences,Input validation chains | HTTP interceptor pipelines | Event handling | Permission checking,Handler → ConcreteHandler,"fun validate(input: Input): Result {\n if (input.name.isBlank()) return Error(""Name required"")\n if (input.email.isInvalid()) return Error(""Invalid email"")\n if (input.age < 18) return Error(""Must be 18+"")\n if (input.password.isWeak()) return Error(""Weak password"")\n return Success\n}","interface Validator {\n var next: Validator?\n fun validate(input: Input): Result\n}\nclass NameValidator : Validator {\n override fun validate(input: Input): Result {\n if (input.name.isBlank()) return Error(""Name required"")\n return next?.validate(input) ?: Success\n }\n}",OkHttp Interceptors are a Chain of Responsibility. Use for composable validation and middleware patterns.,https://refactoring.guru/design-patterns/chain-of-responsibility
|
|
24
|
+
Mediator,Behavioral,All,mediator coordinator communication many-to-many decouple components,Define an object that encapsulates how a set of objects interact reducing many-to-many dependencies,Multiple components directly referencing and calling each other | Spaghetti communication between UI elements,Fragment/ViewController communication | Chat room | Form field dependencies | Multi-pane coordination,Mediator → ConcreteMediator → Colleague,"// Fragments directly calling each other\nclass ListFragment {\n fun onItemClick(id: String) {\n (activity as MainActivity).detailFragment.show(id)\n (activity as MainActivity).filterFragment.highlight(id)\n }\n}","class NavigationMediator {\n private val colleagues = mutableMapOf<String, Fragment>()\n fun notify(sender: String, event: Event) {\n when (event) {\n is ItemSelected -> colleagues[""detail""]?.show(event.id)\n }\n }\n}",Android Navigation Component and Jetpack Compose NavHost act as mediators. In iOS use Coordinator pattern. In Flutter use GoRouter.,https://refactoring.guru/design-patterns/mediator
|
|
25
|
+
Visitor,Behavioral,All,visitor double dispatch type checking sealed class operation,Define new operations on an object structure without changing the classes,Type-checking cascades with is/instanceof/as | Adding operations requires modifying every class in hierarchy,Processing AST nodes | Applying operations to sealed class hierarchies | Analytics event processing,Visitor → ConcreteVisitor → Element,"fun process(node: Node) {\n when (node) {\n is TextNode -> { renderText(node); logText(node); analyticsText(node) }\n is ImageNode -> { renderImage(node); logImage(node); analyticsImage(node) }\n is VideoNode -> { renderVideo(node); logVideo(node); analyticsVideo(node) }\n } // every new operation adds another branch\n}","interface NodeVisitor {\n fun visit(node: TextNode)\n fun visit(node: ImageNode)\n fun visit(node: VideoNode)\n}\nclass RenderVisitor : NodeVisitor { ... }\nclass AnalyticsVisitor : NodeVisitor { ... }",Kotlin sealed classes with when is often simpler than full Visitor for small hierarchies. Use Visitor when operations change more often than types.,https://refactoring.guru/design-patterns/visitor
|
|
26
|
+
Repository,Domain,Android,repository data source abstraction layer clean architecture,Abstract data access behind a clean interface hiding data source details,ViewModel or UI directly calling API/database | Data source logic mixed into presentation layer,Any app with remote + local data | Offline-first apps | Apps needing data caching strategy,Repository → DataSource (Remote + Local),"class UserViewModel {\n suspend fun getUser(id: String): User {\n val response = retrofit.create(UserApi::class.java).getUser(id)\n val user = response.body()!!.toUser()\n roomDb.userDao().insert(user)\n return user\n }\n}","interface UserRepository {\n suspend fun getUser(id: String): User\n}\nclass UserRepositoryImpl @Inject constructor(\n private val remote: UserRemoteDataSource,\n private val local: UserLocalDataSource\n) : UserRepository {\n override suspend fun getUser(id: String): User {\n return local.getUser(id) ?: remote.getUser(id).also { local.save(it) }\n }\n}",Core of Clean Architecture. Repository should return domain models not DTOs. Use Flow for reactive data. Inject via Hilt.,https://developer.android.com/topic/architecture/data-layer
|
|
27
|
+
Repository,Domain,iOS,repository data source abstraction layer clean architecture swift,Abstract data access behind a clean interface hiding data source details,View or ViewModel directly calling URLSession/CoreData | Network logic in SwiftUI views,Any app with network + persistence | Offline-first | Data synchronization,Repository → DataSource (Remote + Local),"class UserViewModel: ObservableObject {\n func getUser(id: String) async {\n let data = try await URLSession.shared.data(from: url)\n let user = try JSONDecoder().decode(User.self, from: data.0)\n try context.save()\n }\n}","protocol UserRepository {\n func getUser(id: String) async throws -> User\n}\nclass UserRepositoryImpl: UserRepository {\n private let remote: UserRemoteDataSource\n private let local: UserLocalDataSource\n func getUser(id: String) async throws -> User {\n if let cached = try? await local.getUser(id: id) { return cached }\n let user = try await remote.getUser(id: id)\n try await local.save(user)\n return user\n }\n}",Use Swift protocols for repository abstraction. Combine with async/await for clean data flow.,https://developer.apple.com/documentation/swiftdata
|
|
28
|
+
Dependency Injection,Creational,Android,dependency injection di hilt dagger constructor inject scope provide,Provide dependencies to objects rather than having them create their own,Manual object creation with constructor calls scattered | Hard-coded dependencies preventing testing,Any app beyond trivial size | Providing repositories to ViewModels | Swapping implementations for testing,Container → Module → Component → Injected class,"class UserViewModel {\n private val repo = UserRepositoryImpl(\n api = RetrofitClient.create(UserApi::class.java),\n db = Room.databaseBuilder(...).build().userDao()\n )\n}","@HiltViewModel\nclass UserViewModel @Inject constructor(\n private val repo: UserRepository\n) : ViewModel() { ... }\n\n@Module @InstallIn(SingletonComponent::class)\nabstract class RepoModule {\n @Binds abstract fun bindUserRepo(impl: UserRepositoryImpl): UserRepository\n}",Use Hilt for Android DI. Prefer constructor injection. Use @Binds over @Provides when possible. Scope carefully.,https://developer.android.com/training/dependency-injection/hilt-android
|
|
29
|
+
Dependency Injection,Creational,Flutter,dependency injection di get_it provider riverpod inject scope provide dart,Provide dependencies to objects rather than having them create their own,Manual object creation inside widgets | Hard-coded service instances,Any app with services/repositories | Testing with mock implementations | Feature modules,Container → Provider → Injected class,"class HomePage extends StatelessWidget {\n final repo = UserRepositoryImpl(\n api: ApiClient(),\n db: LocalDb(),\n );\n}","// With riverpod\nfinal userRepoProvider = Provider<UserRepository>((ref) {\n return UserRepositoryImpl(\n api: ref.read(apiProvider),\n db: ref.read(dbProvider),\n );\n});\n// In widget: ref.read(userRepoProvider)",Use riverpod or get_it for Flutter DI. Provider is simpler but less powerful. Riverpod is compile-safe and testable.,https://riverpod.dev/
|
|
30
|
+
Mapper,Structural,All,mapper data transfer object dto domain model convert transform,Separate data transformation logic from business logic into dedicated mapper classes,Data transformation code mixed into ViewModels or repositories | Duplicated mapping logic,Mapping API DTOs to domain models | Converting domain models to UI state | Database entity mapping,Mapper → Input → Output,"// In ViewModel\nfun loadUser(id: String) {\n val response = api.getUser(id)\n val user = User(\n id = response.id,\n fullName = ""${response.firstName} ${response.lastName}"",\n avatarUrl = response.images.firstOrNull()?.url ?: """"\n )\n}","class UserMapper {\n fun toDomain(dto: UserDto): User = User(\n id = dto.id,\n fullName = ""${dto.firstName} ${dto.lastName}"",\n avatarUrl = dto.images.firstOrNull()?.url ?: """"\n )\n}\n// In Repository: mapper.toDomain(api.getUser(id))",Place mappers at layer boundaries in Clean Architecture. One mapper per DTO-to-domain conversion. Keep them pure functions.,https://developer.android.com/topic/architecture
|
|
31
|
+
Prototype,Creational,All,prototype clone copy duplicate object creation expensive,Create new objects by copying existing ones avoiding expensive creation,Repeated expensive object setup | Creating similar objects with minor variations,Duplicating configured objects | Copying UI component configurations | Cloning game entities,Prototype → clone(),"// Re-creating full config each time\nval config1 = NetworkConfig(timeout = 30, retries = 3, cache = true, interceptors = listOf(...))\nval config2 = NetworkConfig(timeout = 60, retries = 3, cache = true, interceptors = listOf(...)) // same but timeout","data class NetworkConfig(\n val timeout: Int = 30,\n val retries: Int = 3,\n val cache: Boolean = true\n) // Kotlin data class has copy()\nval config2 = config1.copy(timeout = 60)",Kotlin data class copy() is a built-in Prototype. In Dart use copyWith(). In Swift use value types (struct) for implicit copying.,https://refactoring.guru/design-patterns/prototype
|