Comprender el código limpio en Android

Como introducción, me gustaría recordar la cita del tío Bob
Estás leyendo este artículo por dos razones. El primero, usted es un programador, el segundo, quiere ser el mejor programador.
Imagina que est√°s en una biblioteca y buscas algunos libros. Si la biblioteca est√° ordenada, tiene categor√≠as de libros, encontrar√° r√°pidamente el que necesita. Adem√°s, el dise√Īo interior y la arquitectura geniales har√°n que su estancia en esta biblioteca sea bastante c√≥moda para usted.

Al igual que con la escritura de libros, si desea crear algo grandioso, entonces necesita saber cómo escribir y cómo organizar su código. Si tiene miembros del equipo u otra persona que tiene su código (obsoleto), solo necesitan ver los nombres de las variables o paquetes o clases, y lo entenderán de inmediato. No necesitan decir "E ** l" I este código y comenzarlo de nuevo desde cero.

¬ŅQu√© es un c√≥digo limpio?




Traducción
Estimado programador:

Cuando escribí este código, ¡solo Dios y yo sabíamos cómo funciona!
¬°Ahora solo Dios lo sabe!

Antes de intentar optimizar esta rutina y no conducirá al éxito (muy probablemente), aumente el contador de tiempo para advertir al próximo desarrollador

Total de horas dedicadas: 567

Como puede ver, no es suficiente terminar el desarrollo más rápido. Si en el futuro no pueden resolver este código, también se convertirá en una deuda técnica.

Su código está en un estado "Puro" si todos los miembros de su equipo pueden entenderlo. Cualquier desarrollador que no sea el autor original puede leer y mejorar el código limpio. Con la comprensión viene la legibilidad, mutabilidad, extensibilidad y mantenibilidad.

¬ŅDebo ocuparme de esto?


La razón por la que debería preocuparse por la limpieza de su código es porque describe sus pensamientos a otros desarrolladores. Es por eso que debe tener cuidado de que su código sea más elegante, simple y legible.

Se√Īales de c√≥digo limpio


  • Tu c√≥digo debe ser elegante: tu c√≥digo debe hacerte sonre√≠r como una caja de m√ļsica bien hecha o un autom√≥vil bien dise√Īado
  • Su c√≥digo debe estar enfocado: cada funci√≥n, cada clase, cada m√≥dulo implementa una tarea, que permanece completamente no distra√≠da y no contaminada por los detalles circundantes.
  • El c√≥digo no contiene duplicados
  • Pase todas las pruebas con √©xito
  • Minimiz√≥ el n√ļmero de entidades, m√©todos, clases, etc.

La diferencia entre un buen programador y un profesional es que un programador profesional comprende que la comprensión del código es primordial. Un profesional usa este poder para escribir código que todos entienden - Robert C. Martin

Escribe los nombres conscientes


Elegir un buen nombre puede llevar mucho tiempo, pero luego ahorrará más. El nombre de una variable, función, clase debería responder a todas las preguntas importantes. Debería decirle por qué existe, por qué es necesario y cómo usarlo. Si el nombre requiere comentario, el nombre no revela su propósito

Ejemplo simple

// 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() 

Nombres de clase


Las clases y los objetos deben ser sustantivos, como Custom, WikiPage, Account y AddressParser. Evite palabras como Administrador, Procesador, Datos o Información. También recuerde que el nombre de la clase no debe ser un verbo.

Nombres de métodos


Los nombres de los métodos deben ser verbos, por ejemplo postPayment, deletePage o save. Modificadores de acceso, los predicados deben nombrarse por su valor y con el prefijo get, set y de acuerdo con el estándar JavaBean.

Antes de continuar, tome un breve descanso, abastecerse de café y galletas.



Bien, ahora pasemos a los principios S√ďLIDOS.

Escribir código que se adhiera a los principios de SOLID


Estos principios fueron desarrollados por el t√≠o Bob, SOLID es una abreviatura que describe un conjunto de principios dise√Īados para escribir un buen c√≥digo.

Principio de responsabilidad √ļnica (S)


Esto significa que cada clase debe tener una sola responsabilidad. Nunca debe haber m√°s de una raz√≥n para cambiar una clase. No es necesario agregar todo a tu clase, simplemente porque puedes hacerlo. Divide las clases grandes en clases m√°s peque√Īas y evita las clases de Dios.

Un ejemplo:

Tenemos un RecyclerView.Adapter con lógica de negocios dentro de onBindViewHolder

 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) } } 

Esto hace que RecyclerView.Adapter no sea el √ļnico responsable porque contiene l√≥gica de negocios dentro de onBindViewHolder. Este m√©todo solo es responsable de insertar datos en la vista.

El principio de apertura / cierre (O)


Las entidades de software deben estar abiertas para expansión, pero cerradas para modificación. Esto significa que si está desarrollando la clase A y sus colegas querrán cambiar la función dentro de esta clase. Pueden hacerlo fácilmente expandiendo esta clase sin cambiar la clase misma.
Un ejemplo simple es la clase RecyclerView.Adapter. Puede expandirlo f√°cilmente y crear su propio adaptador con un comportamiento no est√°ndar sin modificar el 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) } } 

El principio de sustitución de Barbara Lisk (L)


La clase secundaria debe complementar al padre y no cambiarlo. Esto significa que la subclase debe anular los métodos principales que no violan la funcionalidad de esta clase principal. Por ejemplo, creamos una interfaz de clase que tiene un oyente onClick () y luego aplica el oyente en MyActivity y le da una acción Toast cuando se llama a onClick ().

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

Principio de separación de interfaz


Este principio establece que el cliente no debe depender de métodos que no utiliza.
Esto significa que si desea escribir la clase A y agregarle la funcionalidad de otra clase B. No es necesario redefinir todas las clases A dentro de la clase B.

Ejemplo: en nuestra actividad, necesitamos implementar SearchView.OnQueryTextListener (), pero solo necesitamos el método 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 } }) 

¬ŅC√≥mo hacemos esto? F√°cil! Simplemente cree una devoluci√≥n de llamada y una clase que extienda 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 } } 

Y así lo agregamos a nuestra vista

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

O así usando la función de extensión en 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) 

Principio de inversión de dependencia


Dependencia de abstracciones, sin dependencia de algo específico.
La definición de inversión de dependencia del tío Bob consta de dos conceptos.

Los módulos de nivel superior no deben depender de los módulos de nivel inferior. Ambos deben estar vinculados a las abstracciones. Las abstracciones no deberían depender de los detalles. Los detalles deben depender de las abstracciones. Los módulos de alto nivel que implementan una lógica compleja deberían ser fácilmente reutilizables sin cambios en los módulos de nivel inferior. Para hacer esto, debe ingresar una abstracción que separe los módulos de los niveles superior e inferior entre sí.

Un ejemplo simple de este patrón MVP. Tiene un objeto de interfaz que lo ayuda a comunicarse con clases específicas. Qué se entiende: las clases de IU (Actividad / Fragmento) no necesitan conocer la implementación real de los métodos del presentador. Por lo tanto, si realiza cambios dentro del presentador, las clases de IU no tienen que preocuparse por estos cambios.

Veamos un ejemplo.

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

Y ahora en la actividad

 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() } //.... } 

De esta manera, creamos una interfaz que abstrae la implementación del presentador, y nuestra clase de vista mantiene una referencia a PresenterInterface.

Source: https://habr.com/ru/post/443662/


All Articles