Entendendo o código limpo no Android

Como introdução, gostaria de lembrar a citação do tio Bob
Você está lendo este artigo por dois motivos. Primeiro - você é um programador, segundo - você quer ser o melhor programador.
Imagine que você está em uma biblioteca e procura alguns livros. Se a biblioteca estiver classificada, tiver categorias de livros, você encontrará rapidamente o que precisa. Além disso, o design e a arquitetura de interiores bacanas tornarão a sua estadia nesta biblioteca bastante confortável para você.

Assim como na escrita de livros, se você deseja criar algo ótimo, precisa saber como escrever e como organizar seu código. Se você tiver membros da equipe ou outra pessoa com o seu código (obsoleto), eles precisam apenas ver os nomes das variáveis, pacotes ou classes e entenderão imediatamente. Eles não precisam dizer "E ** l" neste código e iniciá-lo novamente do zero.

O que é um código limpo?




Tradução
Caro programador:

Quando escrevi este código, somente Deus e eu sabíamos como ele funciona!
Agora só Deus sabe disso!

Antes de tentar otimizar essa rotina e ela não resultará em sucesso (provavelmente), aumente o contador de tempo para avisar o próximo desenvolvedor

Total de horas gastas: 567

Como você pode ver, não é suficiente concluir o desenvolvimento mais rapidamente. Se no futuro eles não conseguirem descobrir esse código, ele também se tornará uma dívida técnica.

Seu código está no estado "Puro" se todos os membros da sua equipe puderem entendê-lo. O código limpo pode ser lido e aprimorado por qualquer desenvolvedor que não seja o autor original. Com o entendimento, vem a legibilidade, a mutabilidade, a extensibilidade e a manutenção.

Devo cuidar disso?


O motivo pelo qual você deve se preocupar com a limpeza do seu código é porque descreve seus pensamentos para outros desenvolvedores. É por isso que você deve cuidar para que seu código seja mais elegante, simples e legível.

Sinais de Código Limpo


  • Seu código deve ser elegante: seu código deve fazer você sorrir como uma caixa de música bem feita ou um carro bem projetado
  • Seu código deve ser focado: cada função, cada classe, cada módulo implementa uma tarefa, que permanece completamente não distraída e não poluída pelos detalhes circundantes.
  • O código não contém duplicatas
  • Passe todos os testes com sucesso
  • Minimizou o número de entidades, métodos, classes e assim por diante

A diferença entre um bom programador e um profissional é que ele entende que a compreensibilidade do código é fundamental. Um profissional usa esse poder para escrever código que todos entendem - Robert C. Martin

Escreva os nomes conscientes


A escolha de um bom nome pode levar muito tempo, mas mais tarde economizará mais. O nome de uma variável, função, classe deve responder a todas as perguntas significativas. Deve dizer por que existe, por que é necessário e como usá-lo. Se o nome exigir comentário, o nome não revela sua finalidade

Exemplo simples

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

Nomes de classe


Classes e objetos devem ser substantivos, como Personalizado, WikiPage, Conta e AddressParser. Evite palavras como gerente, processador, dados ou informações. Lembre-se também de que o nome da classe não deve ser um verbo.

Nomes dos métodos


Os nomes dos métodos devem ser verbos, por exemplo, postPayment, deletePage ou save. Modificadores de acesso, predicados devem ser nomeados por seu valor e com o prefixo get, set e de acordo com o padrão JavaBean.

Antes de continuar, faça uma pequena pausa, compre café e biscoitos.



OK, agora vamos aos princípios do SOLID.

Escreva código aderente aos princípios do SOLID


Esses princípios foram desenvolvidos pelo tio Bob, o SOLID é uma abreviação que descreve um conjunto de princípios criados para escrever um bom código.

Princípio (S) de Responsabilidade Única


Isso significa que cada classe deve ter apenas uma responsabilidade. Nunca deve haver mais de um motivo para mudar de classe. Não há necessidade de adicionar tudo à sua turma, simplesmente porque você pode fazê-lo. Divida classes grandes em classes menores e evite Classes de Deus.

Um exemplo:

Temos um RecyclerView.Adapter com lógica de negócios 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) } } 

Isso torna o RecyclerView.Adapter não apenas responsável, pois contém lógica de negócios dentro do onBindViewHolder. Esse método é responsável apenas pela inserção de dados na exibição.

O princípio de abertura / fechamento (O)


As entidades de software devem estar abertas para expansão, mas fechadas para modificação. Isso significa que, se você estiver desenvolvendo a classe A, seus colegas desejarão alterar a função dentro dessa classe. Eles podem fazer isso facilmente expandindo essa classe sem alterar a própria classe.
Um exemplo simples é a classe RecyclerView.Adapter. Você pode expandi-lo facilmente e criar seu próprio adaptador com comportamento não padrão, sem modificar o 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) } } 

O princípio da substituição de Barbara Lisk (L)


A classe filho deve complementar o pai e não alterá-lo. Isso significa que a subclasse deve substituir os métodos pai que não violam a funcionalidade dessa classe pai. Por exemplo, criamos uma interface de classe que possui um ouvinte onClick () e, em seguida, você aplica o ouvinte em MyActivity e executa uma ação Toast quando onClick () é chamado.

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

Princípio de separação de interface


Este princípio afirma que o cliente não deve depender de métodos que ele não usa.
Isso significa que se você deseja escrever a classe A e adicionar a funcionalidade de outra classe B. Não há necessidade de redefinir todas as classes A dentro da classe B.

Exemplo: em nossa atividade, precisamos implementar SearchView.OnQueryTextListener (), mas precisamos apenas do 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 } }) 

Como fazemos isso? Fácil! Basta criar um retorno de chamada e uma classe estendendo 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 } } 

E então adicionamos à nossa visão

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

Ou então, usando a função de extensão no 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) 

Princípio de Inversão de Dependência


Dependência de abstrações, sem dependência de algo específico.
A definição de inversão de dependência no tio Bob consiste em dois conceitos.

Os módulos de nível superior não devem depender dos módulos de nível inferior. Ambos devem estar ligados a abstrações. As abstrações não devem depender dos detalhes. Os detalhes devem depender de abstrações. Os módulos de alto nível que implementam lógica complexa devem ser facilmente reutilizáveis ​​sem alterações nos módulos de nível inferior. Para fazer isso, é necessário inserir uma abstração que separa os módulos dos níveis superior e inferior.

Um exemplo simples desse padrão MVP. Você tem um objeto de interface que ajuda a se comunicar com classes específicas. O que se entende - as classes de interface do usuário (Atividade / Fragmento) não precisam conhecer a implementação real dos métodos do apresentador. Portanto, se você fizer alterações dentro do apresentador, as classes da interface do usuário não precisarão se preocupar com essas alterações.

Vamos dar uma olhada em um exemplo

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

E agora na atividade

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

Dessa maneira, criamos uma interface que abstrai a implementação do apresentador, e nossa classe view mantém uma referência ao PresenterInterface.

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


All Articles