Androidの構造とアプリケーションを本当に理解していますか?

序文

Androidのアーキテクチャに関しては、多くの人がいつも心の中で幻想を抱いていたかもしれません。彼らは理解しているが理解していないようで、それらを使用してどこでも使用しています。この状況の意味は本当に限られています。私は複数のプロジェクトをリファクタリングした経験があり、たまたまデザイン分野に興味を持っています。今日は、建築とデザインについての私の理解を予約なしで皆さんと共有します。
この記事では、MVC、MVP、およびMVVMとは何かについて具体的に説明しませんが、ここで説明するポイントは、これらのパターンの基礎となるはずです。本質的に、これが行われる理由と、そうすることの利点は何であるかを理解しています。これらの根底にあるアイデアの対応するアーキテクチャモデルを見てください。新しい感覚が得られると思います。
知識の予備:Javaオブジェクト指向の6つの設計原則を習得する必要があります。理解できない場合は、大丈夫です。使用されている設計原則について詳しく説明します。

目次

  • 1.モジュール性の重要性は何ですか?
  • 1.1基本的な概念と基本的な考え方
  • 1.2どの機能に基づいてモジュール化する必要がありますか?
  • 1.3 Androidはどのように階層処理を行いますか?
  • 1.4データマッパーは解毒剤かもしれません
  • 1.5ビジネスロジックを配置する場所がない
  • 2.合理的な階層化は、データ駆動型UIへの道を開くことです
  • 2.1制御の反転とは何ですか?
  • 2.2データ駆動型UIとは何ですか?
  • 2.3制御の反転としてのデータ駆動型UIの基本的な考え方はなぜですか?
  • 2.4なぜDiffが導入されるのですか?
  • 3.関数型プログラミングを推奨する理由
  • 3.1関数型プログラミングとは何ですか?
  • 3.2 Androidビューの開発は、関数型プログラミングのアイデアから学ぶことができます

1.モジュール性の重要性は何ですか?

1.1基本的な概念と基本的な考え方

すべてのモジュール化は、単一の設計原則(文字通りの意味を理解できる)、関数、クラス、またはモジュールを満たすことであり、責任が単一であるほど、再利用性が強くなり、同時に間接的に結合
減らすことができます。ソフトウェア工学の背景変更を加えると間違いを犯す可能性があります。人は機械ではないので、「注意を払えば間違いはありません」とは言わないでください。私たちにできることは、モジュールをできるだけ単一にすることです。責任が単一であるほど、外部モジュールに影響を与える可能性が低くなり、エラーの可能性が低くなります。
したがって、モジュール性のコアアイデアは次のとおりです:単一の設計原理

1.2どの機能に基づいてモジュール化する必要がありますか?

モジュラー処理を行う場合は、2つの特性に基づいて機能特性とビジネス特性を実行するようにしてください。
機能特性

ネットワーク、画像の読み込みなどはすべて機能特性と呼ぶことができます。たとえば、ネットワーク:ネットワークフレームワークの統合、パッケージ化などを同じモジュール(モジュール、パッケージなど)に書き込むことができます。これにより、読みやすさが向上し(同じディレクトリが一目でわかります)、確率が低下します。誤操作の、メンテナンスを容易にし、それをより安全にします。同時に、モジュールはMavenライブラリなどのリモートでホストでき、複数のプロジェクトで使用して再利用性をさらに向上させることができます
ビジネス機能
は文字通り理解できますこれは私たちがよく書くビジネスです。
なぜ必要なのです。ビジネス機能に基づいてモジュールを分割するには?ビジネス機能が機能機能よりも優先されると言いますか?
たとえば、次の図に示すように、

多くの人がこの下請け方法を見たり使用したりしていると思います。すべてのアダプタ、プレゼンター、アクティビティなどをビジネス層の対応するパッケージに入れるのは合理的ですか。答えは無理だと言わせてください。まず第一に、これはすでにビジネス層にあります。私たちが行うことはすべて実際にビジネス層にサービスを提供しているので、ビジネスの優先順位を最も高くする必要があります。対応するクラスを同じパッケージ内。
機能モジュールの中核は機能であり、機能ごとにモジュールに分割する必要があります。ビジネスモジュールの中核はビジネスであり、最初にビジネスごとに、次に機能ごとにモジュールに分割する必要があります。

1.3 Androidはどのように階層処理を行いますか?

フロントエンド開発は、ビューに表示する前に実際にデータ処理を行っています。データとビューは2つの異なる概念です。再利用性と保守性を向上させるには、単一の設計原則に従って2つを階層的に処理する必要があります。したがって、MVC、MVP、MVVMのいずれであっても、コアポイントはレイヤーデータとビューです。
つまずき:

一般的に、ネットワークリクエストを通じて取得するデータ構造はすべてバックエンドによって定義されます。つまり、ビューレイヤーはバックエンドによって定義されたフィールドを直接使用する必要があります。バックエンドがビジネス調整を行ったら、次のようにします。次の擬似コードに示すように、フロントエンドをデータレイヤーから開始するように強制します–>ビューレイヤーは対応する変更を行います。

//原始逻辑
数据层
Model{
    
    
    title
}
UI层
View{
    
    
    textView = model.title
}

//后端调整后
数据层
Model{
    
    
    title
    prefix
}
UI层
View{
    
    
    textView = model.prefix + model.title
}

最初、textViewはモデルにタイトルを表示しましたが、バックエンドの調整後、モデルにプレフィックスフィールドを追加する必要があり、textViewの表示コンテンツにも文字列のスプライシングが必要です。ビューレイヤーは、データレイヤーの変更により受動的に変更されます。階層化が完了したので、ビューとデータが互いに干渉しないことを確実に望んでいます。それをどのように解決するのでしょうか。見下ろして...

1.4データマッパーは解毒剤かもしれません

データマッパーはバックエンドで一般的に使用される概念です。通常の状況では、データベースのフィールドを直接使用するのではなく、データマッパー(データマッピング)を追加して、データベーステーブルをオンデマンドでJavaBeanに変換します。この利点また、明らかに、テーブル構造は、トスする方法に関係なく、ビジネスレイヤーコードに影響を与えません。
フロントエンドについては、データマッパーを適切に導入してバックエンドデータをローカルモデルに変換できると思います。ローカルモデルは設計図にのみ対応し、バックエンドビジネスをビューから完全に分離します。これにより、1.3が直面する問題も解決されます。具体的な方法は、次のとおりです。

数据层
Model{
    
    
    title
    prefix
}
本地模型(与设计图一一对应)
LocalModel{
    
    
    //将后端模型转换为本地模型
    title = model.prefix + model.title
}
UI层
View{
    
    
    textView = localModel.title
}

LocalModelは、アダプターパターンを介してデータレイヤーをビューレイヤーから分離する中間レイヤーと同等です。
データマッパーがフロントエンドに導入された後、バックエンドから離れた場所で開発できます。要件が明確である限り、ビューレイヤーの開発を行うことができます。構造やフィールドについて心配する必要はありません。バックエンドから返されます。そして、このアプローチは一度限りで行われます。たとえば、バックエンドは特定のフィールドを調整する必要があります。考えずにデータレイヤーに直接移動できます。関連する調整の100%はビューレイヤーに影響しません。
注:

フロントエンドとバックエンドをより完全に分離するために、一部の企業は現在、フロントエンド開発者によってJava Bean(LocalModelと同等)の構造を提供しています。利点も明らかです。より多くのビジネスがバックエンドにまとまります。結局のところ、アプリのバージョンを公開するコストはまだ比較的高いです。このような状況に直面した場合、実際にはデータマッパーを作成する必要はありません。したがって、どのようなアーキテクチャ設計も実際の状況と組み合わせる必要があり、自分に合ったものが最適です。

1.5ビジネスロジックを配置する場所がない

ビジネスロジックは実際には非常に一般的な概念です。コードの任意の行をビジネスロジックと呼ぶこともできます。このような広い概念をどのように理解しますか?大まかに2つの側面に分けてみましょう。

インターフェイスインタラクションロジック:ジェスチャコントロール、天井サスペンションなどのビューレイヤーのインタラクションロジックは、ビジネスニーズに応じて実装されるため、厳密に言えば、この部分もビジネスロジックです。ただし、ビジネスロジックのこの部分は、通常、ビューレイヤーに実装されます。

データロジック:この部分は、誰もがよく話すビジネスロジックです。さまざまなユーザータイプに応じてさまざまなデータを取得したり、さまざまなインターフェイスを表示したり、一連のデータマッパー操作をバックエンドに追加して支援したりするなど、強力なビジネスロジックです。それらが残りを構成します。それは単なる論理です。理解を容易にするために、以下ではデータロジックをまとめてビジネスロジックと呼びます。
Androidの開発にはデータレイヤーとビューレイヤーが必要であると前述しましたが、ビジネスロジックに適したレイヤーはどれですか?たとえば、MVVMモードでは、ビジネスロジックはViewModelで処理されると誰もが言っているので、大きな問題ではありませんが、インターフェイスが十分に複雑な場合、対応するViewModelコードには数百または数千の行が含まれる可能性があります。肥大化したようです。読みやすさも非常に悪いです。最も重要な点は、これらのビジネスは単体テストケースを作成するのが難しいということです。
ビジネスロジックに関しては、処理用に別のユースケースを作成することをお勧めします。
ユースケースは通常、ViewModelerとデータレイヤーの間に配置されます。ビジネスロジックとデータマッパーはユースケースに配置する必要があります。各動作はユースケースに対応します。これにより、肥大化したViewModelerの問題が解決され、テストケースの作成が容易になります。
注意点:
優れた設計とは、特定のシナリオで特定の問題を解決することです。過剰設計は、問題を解決するだけでなく、開発コストを増加させます。私の現在の経験によると、Android開発のシーンの少なくとも半分は非常に単純です:リクエスト->データの取得->ビューのレンダリングとデータマッパーの追加プロセスは非常に単純で、後の変更はそれほど大きくないかもしれません。そうではありません。ユースケースを作成する必要があり、データマッパーをデータレイヤーにスローできます。

2.合理的な階層化は、データ駆動型UIへの道を開くことです

結論から始めましょう:データ駆動型UIの本質は制御の反転です

2.1制御の反転とは何ですか?

制御とは、プログラムフローの制御を指し、通常、開発者によって実行されます。このプロセスは制御です。ただし、開発者は人間であるため、エラーは避けられません。現時点では、役割を逆にすることができ、成熟したフレームワークがプロセス全体を担当します。プログラマーは、フレームワークによって予約された拡張ポイントに独自のビジネスコードを追加するだけで済みます。 。フレームワークを使用してプログラムフロー全体の実行を駆動すると、このプロセスが逆になります。
制御の反転の概念は、依存関係の抽象化が欠落していることを除いて、設計原則の依存性逆転に似ています。
類推するには:

既存のHTTPリクエスト要件があります。HTTTリンクを自分で維持し、TCPソケットを自分で管理し、HTTPキャッシュを自分で処理する場合、つまり、HTTPプロトコル全体が自分でカプセル化されます。このプロジェクトかどうかは言うまでもありません。個人で実装できますが、実装しても抜け穴がたくさんあります。この時点で、考え方を変えることができます。OkHttpを使用して、OkHttpは成熟したフレームワークであり、基本的にエラーはありません。個人はHTTPプロトコルをカプセル化してOkHttpフレームワークを使用します。このプロセスはHTTPを制御する役割が逆転しました。個人—>成熟したフレームワークOkHttpは制御の反転です。利点も明らかです。フレームワークエラーの可能性は非常に高いです。個人よりも低い。

2.2データ駆動型UIとは何ですか?

素人の言葉で言えば、データが変更されると、対応するUIも変更する必要があります。逆に、UIを変更する必要がある場合は、対応するデータを変更するだけで済みます。Flutter、Compose、Vueなどの最も人気のあるUIフレームワークは、基本的に関数型プログラミングに基づいてデータ駆動型UIを実装します。これらの共通の目的は、データとUIの一貫性の問題を解決することです。
現在のAndroidでは、DataBindingを使用して同じ効果を実現できます。例としてJetpack MVVMを取り上げます。ViewModelはリポジトリからデータを取得し、ViewModelに対応するObservableFiledに一時的に保存して、データ駆動型UIを実現しますが、リポジトリから取得したデータを直接使用できることが前提です。アクティビティまたはアダプターで二次データ処理を行ってからUIに通知すると、データ駆動型UIのコアアイデアに違反しています。したがって、データ駆動型UIを実装する場合は、適切な階層化が必要です(UI層によって取得されたデータは処理する必要がなく、直接使用できます)。データマッパーはこの問題を解決するだけで、次のことができます。また、多数のBindAdapterを作成するという現在の状況を回避します。

DataBindingは関数型プログラミングではなく、AbstractProcessorを介して中間コードを生成し、データをXMLにマップするだけです。

2.3制御の反転としてのデータ駆動型UIの基本的な考え方はなぜですか?

現在、AndroidエコシステムにはデータバインディングUIを実装できるフレームワークが2つしかありません。DataBindingとCompose(まだ説明していません)
。DataBindingを導入する前にデータをレンダリングするには、通常、次の2つの手順が必要です。

var title = "iOS"
fun setTitle(){
    
    
     //第一步更改数据源
     title = "Android"
     //第二个更改UI
     textView = title
}

データソースの変更とUIの変更には、合計2つの手順が必要です。データソースとUIのいずれかを変更するのを忘れると、バグが発生します。「両方を変更するのを忘れない」と言わないでください。複雑なロジックに直面し、数十または数十のデータソースでさえエラーがないことを保証することは困難です。この種の問題は、DataBindingによって解決できます。対応するObservableFiledUIを変更するだけで、同期的に変更されます。コントロールUIの状態も、個人からDataBindingに戻されます。DataBindingでは個人的な過失は発生しません。
したがって、データ駆動型UIの基本的な考え方は、制御の反転です

2.4なぜDiffが導入されるのですか?

diffの導入前:

RecyclerViewは動的な削除、追加、更新を実現したいと考えています。データとUIを手動で更新する必要があるため、途中にピースを挿入してデータとUIを個別に更新すると、前述のデータ駆動型UIに違反します。削除に関係なく、追加または更新するエントリは1つだけです。データソースを変更する限り、UIが更新されます。この原則を満たすには、データソースを変更した後にのみRecyclerViewを更新できますが、これによりパフォーマンスの問題が発生し、複雑なインターフェイスは明らかにCatonのように感じられます。
diffの導入後:
Diffアルゴリズムは、oldItemとnewItemの違いを比較することにより、変更されたアイテムを自動的に更新します。同時に、アニメーション効果の削除と追加をサポートします。この機能は、データを実装する必要があるRecyclerViewのパフォーマンスの問題を解決します。駆動型UI。

3関数型プログラミングを推奨する理由

3.1関数型プログラミングとは何ですか?

  • 1つの入口、1つの出口。
  • 機能チェーン内の操作自体に関連しない操作を実行しないでください
  • 関数チェーン内で外部変数を使用しないでください(実際、これは準拠するのが難しく、適切に壊すことができます)
    人気のある点は、初期値が与えられると、関数の操作後に目標値が得られるということです。チェーンであり、操作中に外部からの介入はありません。許可と同時に、それ自体に関係のない操作を行わないでください。これにより、予期しないエラーの発生が根本的に解決されます。
    例えば:
//Kotlin代码

listOf(10, 20).map {
    
    
   it + 1
}.forEach {
    
    
   Log.i("list", "$it")
}

上記のチェーンプログラミングは標準の関数型プログラミングです。開発者は入力と出力の間に介入する機会がないため(つまり、Log.i(...)の前に、開発者はリストを処理する権限がありません)、プロセス全体が100%安全です。 。、RxJava、Flow、および連鎖された高次関数はすべて標準の関数型プログラミングです。これらは仕様レベルからデータセキュリティの問題を解決します。したがって、Kotlin(RxJava、Kotlin Flowも)でデータを処理するときは、可能な限り連鎖した高階関数を使用することをお勧めします。

3.2 Androidビューの開発は、関数型プログラミングのアイデアから学ぶことができます

Androidビューの開発は、主に次のプロセスに従います。リクエスト->データの処理-> UIのレンダリング。このプロセスは、関数型プログラミングから学習し、リクエストを入口、レンダリングを出口とし、現在の動作に関係のないことを行わないようにします。このプロセスで(これには、ViewModelとRepositoryの関数も単一の原則に準拠する必要があります)。これは反例であると言うのは少し一般的です。

    View{
    
    
        //刷新
        fun refresh(){
    
    
            ViewModel.load(true)
        }
        //加载更多
        fun loadMore(){
    
    
            ViewModel.load(false)
        }
    }

    ViewModel{
    
    
        //加载数据
        load(isRefresh){
    
    
            if (isRefresh){
    
    
                //刷新
            }else{
    
    
                //加载更多
            }
        }
    }

ビューレイヤーには、更新とロードの2つの動作があります。ロード(isRefresh)には、1つのエントリと2つの出口があります。問題は明らかです。更新を変更したり、さらにロードしたりすると、相手に影響があります。これは、開始と終了の原則の終了に違反します(変更に近い:動作が変更されていない場合、ソースコードを変更することはできません)。 、予測できない問題が発生します。関数型プログラミングのアイデアから学び、ViewModelのロード関数をrefreshとloadMoreに分割して、2種類の動作、2つの入口、2つの出口が互いに干渉しないようにし、関数の接続を増やすことができます。 2つの独立したビジネスチェーンを形成します。
関数型プログラミングは、標準化されたコードを書くことを制約する可能性があります。関数型プログラミングを使用できないシナリオに直面した場合、関数型プログラミングに近づくように制約を試みることができ、ほぼ同じ効果を達成できます。

要約すれば

  • 合理的な階層化により、再利用性が向上し、モジュール間の結合が減少します
  • データマッパーは、開発のためにビューレイヤーをバックエンドから分離することができます
  • 複雑なビジネスロジックをユースケースで記述する必要があります
  • データ駆動型UIの本質は、制御の反転です。
  • 関数型プログラミングにより、より安全なコードを記述できます

おすすめ

転載: blog.csdn.net/A_pyf/article/details/115218115