مرحبا بالجميع. في الآونة الأخيرة ، تم كتابة عدد غير قليل من المقالات حول موضوع العمارة النظيفة. أي بنية نظيفة تسمح لك بكتابة التطبيقات المناسبة للصيانة والاختبار. يمكنك أن تقرأ عن العمارة النقية نفسها في مقالات رائعة مثل:
المفاهيم الخاطئة للهندسة المعمارية النظيفة أو
العمارة النظيفة ، لذلك لا أرى أي سبب لتكرار ما تم كتابته بالفعل.
في البداية ، اسمحوا لي أن أقدم نفسي ، اسمي كاكوشيف رسول. حدث ذلك أنني شاركت في وقت واحد في التنمية المحلية على iOS و Android ، بالإضافة إلى تطوير رمز الواجهة الخلفية لتطبيقات الهاتف المحمول في Navibit. لا تزال هذه شركة غير معروفة ، وهي تستعد لدخول السوق لبيع مواد البناء. لدينا فريق صغير للغاية ، وبالتالي فإن تطوير تطبيقات الهاتف المحمول يقع بالكامل على كتفي (غير المحترف بعد).
في عملي ، غالبًا ما أضطر إلى إنشاء تطبيق واحد على iOS و Android ، وكما تفهم ، بسبب الاختلافات في الأنظمة الأساسية ، غالبًا ما أضطر إلى كتابة نفس الوظيفة عدة مرات. يستغرق هذا الأمر وقتًا طويلاً ، لذا عندما التقيت بهندسة نظيفة منذ فترة ، توصلت إلى الفكرة التالية: لغة kotlin واللغات السريعة متشابهة تمامًا ، ولكن الأنظمة الأساسية مختلفة ، ولكن في الهندسة المعمارية النظيفة هناك طبقة مجال غير مرتبطة بالمنصة ، ولكنه يحتوي على منطق تجاري خالص. ماذا سيحدث إذا أخذت طبقة النطاق بالكامل من android وقمت بنقلها إلى iOS ، مع الحد الأدنى من التغييرات؟
حسنا ، تصور - فعلت. لقد بدأت التحويل. وبالفعل اتضح أن الفكرة صحيحة في الغالب. احكم بنفسك. على سبيل المثال ، فيما يلي تفاعل واحد على kotlin و swift:
Kotlin (Android)class AuthInteractor @Inject internal constructor(private val authRepository: AuthRepository, private val profileRepository: ProfileRepository) { fun auth(login: String, password: String, cityId: Int): Single<Auth> = authRepository.auth(login.trim { it <= ' ' }, password.trim { it <= ' ' }, cityId, cloudToken) fun restore(login: String, password: String, cityId: Int, confirmHash: String): Single<AuthInfo> = authRepository.restore(login.trim { it <= ' ' }, password.trim { it <= ' ' }, cityId, confirmHash) fun restore(password: String, confirmHash: String): Single<AuthInfo> = authRepository.restore(password.trim { it <= ' ' }, confirmHash) fun getToken(): String = authRepository.checkIsAuth() fun register(login: String, family: String, name: String, password: String, cityId: Int, confirmHash: String): Single<AuthInfo> = authRepository.register(login.trim { it <= ' ' }, family.trim { it <= ' ' }, name.trim { it <= ' ' }, password.trim { it <= ' ' }, cityId, confirmHash) fun checkLoginAvailable(login: String): Single<LoginAvailable> = authRepository.checkLoginAvailable(login) fun saveTempCityInfo(authCityInfo: AuthCityInfo?) = authRepository.saveTempCityInfo(authCityInfo) fun checkPassword(password: String): Single<AuthInfo> = authRepository.checkPassword(password) fun auth(auth: Auth) { authRepository.saveToken(auth.token!!) profileRepository.saveProfile(auth.name!!, auth.phone!!, auth.location!!) } companion object { const val AUTH_ERROR = "HTTP 401 Unauthorized" } }
Swift (iOS): class AuthInteractor { public static let AUTH_ERROR = "HTTP 401 Unauthorized" private let authRepository: AuthRepository private let profileRepository: ProfileRepository private let cloudMessagingRepository: CloudMessagingRepository init(authRepository: AuthRepository, profileRepository: ProfileRepository, cloudMessagingRepository: CloudMessagingRepository) { self.authRepository = authRepository self.profileRepository = profileRepository self.cloudMessagingRepository = cloudMessagingRepository } func auth(login: String, password: String, cityId: Int) -> Observable<Auth> { return authRepository.auth(login: login.trim(), password: password.trim(), cityId: cityId, cloudMessagingToken: cloudMessagingRepository.getCloudToken()) } func restore(login: String, password: String, cityId: Int, confirmHash: String) -> Observable<AuthInfo> { return authRepository.restore(login: login.trim(), password: password.trim(), cityId: cityId, confirmHash: confirmHash) } func restore(password: String, confirmHash: String) -> Observable<AuthInfo> { return authRepository.restore(password: password.trim(), confirmHash: confirmHash) } func getToken() -> String { return authRepository.checkIsAuth() } func register(login: String, family: String, name: String, password: String, cityId: Int, confirmHash: String) -> Observable<AuthInfo> { return authRepository.register(login: login.trim(), family: family.trim(), name: name.trim(), password: password.trim(), cityId: cityId, confirmHash: confirmHash) } func checkLoginAvailable(login: String) -> Observable<LoginAvailable> { return authRepository.checkLoginAvailable(login: login) } func saveTempCityInfo(authCityInfo: AuthCityInfo?) { authRepository.saveTempCityInfo(authCityInfo: authCityInfo) } func checkPassword(password: String) -> Observable<AuthInfo> { return authRepository.checkPassword(password: password) } func auth(auth: Auth) { authRepository.saveToken(token: auth.token) profileRepository.saveProfile(name: auth.name, phone: auth.phone, location: auth.location) } }
أو فيما يلي مثال على شكل واجهات المستودعات على منصات مختلفة:
Kotlin (Android) interface AuthRepository { fun auth(login: String, password: String, cityId: Int, cloudMessagingToken: String): Single<Auth> fun register(login: String, family: String, name: String, password: String, cityId: Int, confirmHash: String): Single<AuthInfo> fun restore(login: String, password: String, cityId: Int, confirmHash: String): Single<AuthInfo> fun restore(password: String, confirmHash: String): Single<AuthInfo> fun checkLoginAvailable(login: String): Single<LoginAvailable> fun sendCode(login: String): Single<CodeCheck> fun checkCode(hash: String, code: String): Single<CodeConfirm> fun checkIsAuth(): String fun saveToken(token: String) fun removeToken() fun notifyConfirmHashListener(confirmHash: String) fun getResendTimer(time: Long): Observable<Long> fun checkPassword(password: String): Single<AuthInfo> fun saveTempCityInfo(authCityInfo: AuthCityInfo?) fun saveTempConfirmInfo(codeConfirmInfo: CodeConfirmInfo) fun getTempCityInfo(): AuthCityInfo? fun getConfirmHashListener(): Observable<String> fun getTempConfirmInfo(): CodeConfirmInfo? }
Swift (iOS): protocol AuthRepository { func auth(login: String, password: String, cityId: Int, cloudMessagingToken: String) -> Observable<Auth> func register(login: String, family: String, name: String, password: String, cityId: Int, confirmHash: String) -> Observable<AuthInfo> func restore(login: String, password: String, cityId: Int, confirmHash: String) -> Observable<AuthInfo> func restore(password: String, confirmHash: String) -> Observable<AuthInfo> func checkLoginAvailable(login: String) -> Observable<LoginAvailable> func sendCode(login: String) -> Observable<CodeCheck> func checkCode(hash: String, code: String) -> Observable<CodeConfirm> func checkIsAuth() ->String func saveToken(token: String) func removeToken() func notifyConfirmHashListener(confirmHash: String) func getResendTimer(time: Int) -> Observable<Int> func checkPassword(password: String) -> Observable<AuthInfo> func saveTempCityInfo(authCityInfo: AuthCityInfo?) func saveTempConfirmInfo(codeConfirmInfo: CodeConfirmInfo) func getTempCityInfo() -> AuthCityInfo? func getConfirmHashListener() -> Observable<String> func getTempConfirmInfo() -> CodeConfirmInfo? }
الوضع مشابه لطبقة العرض التقديمي ، حيث أن العروض وواجهات العرض على كلتا المنصتين هي نفسها. لذلك ، وبفضل هذا النقل ، تضاعفت سرعة تطوري تقريبًا ، لأنه نظرًا لكون طبقات النطاق والعرض قد تم تشكيلها بالكامل بالفعل على كلا النظامين الأساسيين ، فإن الشيء الوحيد المتبقي هو ربط مكتبات محددة وتعديل طبقات واجهة المستخدم والبيانات.
شكرا للقراءة حتى النهاية. آمل أن تفيد هذه المقالة مطوري الهواتف المحمولة الذين يشاركون في التنمية المحلية. كل التوفيق.