Androidのクリーンコードについて

はじめに、 ボブおじさんの引用を思い出したい

この記事は2つの理由で読んでいます。 最初の-あなたはプログラマであり、2番目の-あなたは最高のプログラマになりたい。
あなたが図書館にいて、いくつかの本を探していると想像してください。 ライブラリが並べ替えられ、書籍のカテゴリがある場合、必要なものがすぐに見つかります。 さらに、クールなインテリアデザインとアーキテクチャにより、このライブラリでの滞在が非常に快適になります。



本を書くのと同様に、すばらしいものを作成したい場合は、コードの書き方と整理方法を知る必要があります。 チームメンバまたは(旧式の)コードを持っている他の人がいる場合は、変数名またはパッケージまたはクラスを見るだけですぐに理解できます。 このコードを「E ** l」と言って、最初からやり直す必要はありません。



クリーンコードとは何ですか?







翻訳
親愛なるプログラマー:



このコードを書いたとき、神と私だけがどのように機能するかを知っていました!

今、神のみがこれを知っています!



このルーチンを最適化し、成功につながらない可能性が高い(ほとんどの場合)前に、時間カウンターを増やして次の開発者に警告してください



総滞在時間:567



ご覧のとおり、開発を迅速に完了するだけでは不十分です。 将来、このコードを理解できない場合、これも技術的負債になります。



チームのすべてのメンバーがコードを理解できる場合、コードは「クリーン」な状態です。 クリーンなコードは、元の作者以外の開発者が読んで改善できます。 理解には、読みやすさ、可変性、拡張性、保守性が伴います。



これを大事にすべきですか?



コードのクリーンさを気にする必要があるのは、自分の考えを他の開発者に説明するためです。 そのため、コードがよりエレガントでシンプルで読みやすいように注意する必要があります。



クリーンコードの兆候





優れたプログラマーとプロの違いは、プロのプログラマーがコードの理解が最も重要であることを理解していることです。 専門家はこの力を使用して、誰もが理解できるコードを記述します-Robert C. Martin

意識的な名前を書く



適切な名前を選択するには時間がかかりますが、後でもっと節約できます。 変数、関数、クラスの名前はすべての重要な質問に答える必要があります。 なぜ存在するのか、なぜ必要なのか、そしてどのように使用するのかを教えてくれるはずです。 名前にコメントが必要な場合、名前はその目的を明らかにしません



簡単な例



// Bad variables naming var a = 0 // user ages var w = 0 // user weight var h = 0 // user height // Bad functions naming fun age() fun weight() fun height() // Bad classes naming to get user data class UserInfo() // Best practices varibales naming var userAge = 0 var userWeight = 0 var userHeight = 0 // Best practices functions naming fun setUserAge() fun setUserWeight() fun setUserHeight() // Best practices classes naming to get user data class Users()
      
      





クラス名



クラスとオブジェクトは、Custom、WikiPage、Account、AddressParserなどの名詞でなければなりません。 マネージャー、プロセッサー、データ、情報などの言葉は避けてください。 クラス名は動詞であってはならないことも忘れないでください。



メソッド名



メソッド名は、例えばpostPayment、deletePage、saveなどの動詞でなければなりません。 アクセス修飾子、述語は、JavaBean標準に従って、値と接頭辞get、setで命名する必要があります。



続行する前に、少し休憩を取り、コーヒーとクッキーを用意してください。







では、SOLIDの原則に移りましょう。



SOLID原則に準拠したコードを書く



これらの原則はボブおじさんによって開発されました。SOLIDは、良いコードを書くために設計された一連の原則を説明する略語です。



単一責任原則(S)



これは、各クラスが1つの責任のみを負うことを意味します。 クラスを変更する理由が複数あることはありません。 クラスにすべてを追加する必要はありません。単にできるからです。 大きなクラスを小さなクラスに分割し、神のクラスを避けます。



例:



onBindViewHolder内にビジネスロジックを持つRecyclerView.Adapterがあります



 class MyAdapter(val friendList: List<FriendListData.Friend>) : RecyclerView.Adapter<CountryAdapter.MyViewHolder>() { inner class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) { var name: TextView = view.findViewById(R.id.text1) var popText: TextView = view.findViewById(R.id.text2) } override fun onBindViewHolder(holder: MyViewHolder, position: Int) { val friend = friendList[position] val status = if(friend.maritalStatus == "Married") { "Sold out" } else { "Available" } holder.name.text = friend.name holder.popText.text = friend.email holder.status.text = status } override fun getItemCount(): Int { return friendList.size } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.item_friendlist, parent, false) return MyViewHolder(view) } }
      
      





これにより、onBindViewHolder内にビジネスロジックが含まれるため、RecyclerView.Adapterが単独で責任を負うことはありません。 このメソッドは、ビューへのデータの挿入のみを担当します。



開放性/閉鎖性の原理(O)



ソフトウェアエンティティは拡張のために開かれている必要がありますが、変更のために閉じられている必要があります。 これは、クラスAを開発しているときに、同僚がこのクラス内の関数を変更したいと思うことを意味します。 クラス自体を変更せずにこのクラスを拡張することにより、簡単にこれを実行できます。

簡単な例は、RecyclerView.Adapterクラスです。 それを簡単に拡張し、RecyclerView.Adapter自体を変更せずに非標準の動作で独自のアダプターを作成できます。



 class FriendListAdapter(val friendList: List<FriendListData.Friend>) : RecyclerView.Adapter<CountryAdapter.MyViewHolder>() { inner class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) { var name: TextView = view.findViewById(R.id.text1) var popText: TextView = view.findViewById(R.id.text2) } override fun onBindViewHolder(holder: MyViewHolder, position: Int) { val friend = friendList[position] holder.name.text = friend.name holder.popText.text = friend.email } override fun getItemCount(): Int { return friendList.size } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.item_friendlist, parent, false) return MyViewHolder(view) } }
      
      





バーバラリスク代替原理(L)



子クラスは親を補完するものであり、変更しないでください。 つまり、サブクラスは、この親クラスの機能に違反しない親メソッドをオーバーライドする必要があります。 たとえば、onClick()リスナーを持つクラスインターフェイスを作成し、MyActivityでリスナーを適用して、onClick()が呼び出されたときにToastアクションを与えます。



 interface ClickListener { fun onClick() } class MyActivity: AppCompatActivity(), ClickListener { //........ override fun onClick() { // Do the magic here toast("OK button clicked") } }
      
      





インターフェース分離の原理



この原則は、クライアントが使用しないメソッドに依存してはならないことを示しています。

つまり、クラスAを記述し、別のクラスBの機能を追加する場合、クラスB内ですべてのクラスAを再定義する必要はありません。



例:アクティビティでは、SearchView.OnQueryTextListener()を実装する必要がありますが、onQuerySubmit()メソッドのみが必要です。



 mSearchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener{ override fun onQueryTextSubmit(query: String?): Boolean { // Only need this method return true } override fun onQueryTextChange(query: String?): Boolean { // We don't need to implement this method return false } })
      
      





これをどうやってやるの? 簡単! コールバックとSearchView.OnQueryTextListener()を拡張するクラスを作成するだけです



 interface SearchViewQueryTextCallback { fun onQueryTextSubmit(query: String?) } class SearchViewQueryTextListener(val callback: SearchViewQueryTextCallback): SearchView.OnQueryTextListener { override fun onQueryTextSubmit(query: String?): Boolean { callback.onQueryTextSubmit(query) return true } override fun onQueryTextChange(query: String?): Boolean { return false } }
      
      





そしてそれをビューに追加します



 val listener = SearchViewQueryTextListener( object : SearchViewQueryTextCallback { override fun onQueryTextSubmit(query: String?) { // Do the magic here } } ) mSearchView.setOnQueryTextListener(listener)
      
      





または、Kotlinの拡張機能を使用して



 interface SearchViewQueryTextCallback { fun onQueryTextSubmit(query: String?) } fun SearchView.setupQueryTextSubmit (callback: SearchViewQueryTextCallback) { setOnQueryTextListener(object : SearchView.OnQueryTextListener{ override fun onQueryTextSubmit(query: String?): Boolean { callback.onQueryTextSubmit(query) return true } override fun onQueryTextChange(query: String?): Boolean { return false } }) }
      
      





 val listener = object : SearchViewQueryTextCallback { override fun onQueryTextSubmit(query: String?) { // Do the magic here } } mSearchView.setupQueryTextSubmit(listener)
      
      





依存関係の逆転の原則



特定のものに依存せずに、抽象化に依存します。

アンクルボブへの依存関係の逆転の定義は、2つの概念で構成されています。



上位モジュールは下位モジュールに依存しないでください。 両方を抽象化に結び付ける必要があります。 抽象化は詳細に依存すべきではありません。 詳細は抽象化に依存する必要があります。 複雑なロジックを実装する高レベルモジュールは、低レベルモジュールを変更せずに簡単に再利用できる必要があります。 これを行うには、上位レベルと下位レベルのモジュールを互いに分離する抽象化を入力する必要があります。



このMVPパターンの簡単な例。 特定のクラスとの通信に役立つインターフェイスオブジェクトがあります。 意味-UIクラス(アクティビティ/フラグメント)は、プレゼンターメソッドの実際の実装を知る必要はありません。 したがって、プレゼンター内で変更を行う場合、UIクラスはこれらの変更を心配する必要はありません。



例を見てみましょう



 interface UserActionListener { fun getUserData() } class UserPresenter : UserActionListener() { // ..... override fun getUserData() { val userLoginData = gson.fromJson(session.getUserLogin(), DataLogin::class.java) } // ..... }
      
      





そして今活動中



 class UserActivity : AppCompatActivity() { //..... val presenter = UserPresenter() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Activity doesn't need to know how presenter works // for fetching data, it just know how to call the functions // So, if you add method inside presenter, it won't break the UI. // even the UI doesn't call the method. presenter.getUserData() } //.... }
      
      





このようにして、プレゼンターの実装を抽象化するインターフェイスを作成し、ビュークラスはPresenterInterfaceへの参照を維持します。



All Articles