Connect with us

Программирование

Что такое Чистый код и как его можно использовать в Android-разработке

Есть две вещи: программирование и хорошее программирование. Программирование — это то, чем мы все занимаемся. Пришло время заняться хорошим программированием.

Опубликовано

/

     
     
Чистый код

Есть две вещи: программирование и хорошее программирование. Программирование — это то, чем мы все занимаемся. Пришло время заняться хорошим программированием. Все мы знаем, что даже плохой код работает. Но чтобы сделать программу хорошей, нужны время и ресурсы. Но заняться своим кодом никогда не поздно.

Книга Роберта Мартина “Чистый код” дала мне много знаний о лучших практиках и о том, как на самом деле писать код. Теперь мне стыдно за свои навыки программирования. Хотя я всегда старался улучшить свой код, эта книга научила меня гораздо большему.

Вы читаете эту статью по двум причинам. Во-первых, вы программист. Во-вторых, вы хотите стать лучшим программистом.

Что такое чистый код

Недостаточно быстро закончить разработку, если другие не смогут понять ваш код, потому что это станет еще одним техническим долгом.

Ваш код «чистый», если его легко понять всем в команде. Чистый код может быть прочитан и улучшен другим разработчиком. С понятностью приходит удобочитаемость, изменяемость, расширяемость и поддерживаемость.

Причина, по которой вы должны заботиться о своем коде, заключается в том, что он описывает ваш мыслительный процесс другим. Вот почему вы должны начать думать о том, чтобы сделать свой код более элегантным, простым и читаемым.

Характеристики чистого года

  1. Он должен быть элегантным — чистый код должно быть приятно читать. Его чтение должно вызвать у вас улыбку, как это сделала бы хорошо сделанная музыкальная шкатулка или хорошо спроектированный автомобиль.
  2. Чистый код сфокусирован — каждая функция, каждый класс, каждый модуль демонстрируют целеустремленность, которая никогда не размывается и не загрязняется окружающими подробностями.
  3. Чистый код это забота. Кто-то нашел время, чтобы сделать это простым и упорядоченным. Люди уделили должное внимание деталям. Они позаботились.
  4. Он проходит все тесты.
  5. В нем нет дубликатов.
  6. Сведено к минимуму количество сущностей, таких как классы, методы, функции и т.п.

Одно из различий между умным программистом и профессиональным программистом состоит в том, что профессионал понимает, что ясность — главное. Профессионалы используют свои способности во благо и пишут код, понятный другим. — Роберт С. Мартин

Чистый код

Как писать чистый код?

Осмысленные имена

Выбор хороших имен требует времени, но экономит больше, чем на это тратится. Имя переменной, функции или класса должно ответить на все важные вопросы. Оно должно рассказать вам, почему существует объект, для чего он нужен и как он используется. Если имя требует комментария, то оно не раскрывает его намерений.

Вот пример:

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

— Имена классов

Классы и объекты должны иметь имена существительных или фразы, такие как Customer, WikiPage, Account и AddressParser. Избегайте таких слов, как «Менеджер», «Процессор», «Данные» или «Информация» в названии класса. Имя класса не должно быть глаголом.

— Имена методов

Методы должны иметь глагольные имена или глагольные фразы, такие как postPayment, deletePage или save. Аксессоры, мутаторы и предикаты должны называться в соответствии с их значением и иметь префикс get, set и соответствовать стандарту.

— Используйте доменные имена в случае проблемы

Когда для того, что вы делаете, нет «определения», используйте имя из проблемной области. По крайней мере, программист, обслуживающий ваш код, может спросить эксперта в предметной области, что это означает.

Написание кода с использованием принципов S.O.L.I.D.

Эти принципы изобретены Робертом К. Мартином (дядя Боб), SOLID — это термин, описывающий набор принципов для хорошего кода.

Принцип единой ответственности — SRP

Это означает, что у каждого класса должна быть одна цель. У класса никогда не должно быть более одной причины для изменения. То, что вы можете добавить в свой класс все, что хотите, не означает, что вы должны это делать. Разделите большие классы на более мелкие и избегайте классов Бога.

Возьмем пример. У нас есть RecyclerView.Adapter с бизнес-логикой внутри 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)
}
}
view raw MyAdapter.kt hosted with ❤ by GitHub

У RecyclerView.Adapter нет единой ответственности, потому что у него есть бизнес-логика внутри onBindViewHolder. Хотя этот метод отвечает только за данные в своей реализации биндинга представления.

Принцип открыт-закрыт — OCP

Программные объекты должны быть открыты для расширения, но закрыты для модификации. Это означает, что если вы напишете класс A, а затем ваши товарищи по команде захотят внести изменения в функцию внутри класса A, они могут легко сделать это, расширив класс A, вместо того, чтобы вносить изменения внутри класса 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)
}
}

Принцип замен Лискова — LSP

Дочерние классы никогда не должны нарушать определения типов родительского класса.

Это означает, что подкласс должен переопределять методы родительского класса так, чтобы не нарушать функциональность родительского класса. Например, вы создаете класс интерфейса с прослушивателем onClick(), а затем применяете прослушиватель в MyActivity и передаете ему всплывающее тост при вызове onClick().

interface ClickListener {
    fun onClick()
}
class MyActivity: AppCompatActivity(), ClickListener {

    //........
    override fun onClick() {
        // Do the magic here
        toast("OK button clicked")
    }

}

Принцип разделения интерфейса — ISP

Принцип разделения интерфейса (ISP) гласит, что ни один клиент не должен зависеть от методов, которые он не использует.

Это означает, что если вы хотите создать класс A и реализовать его в другом классе (класс B), он не должен переопределять все методы класса A внутри класса B. Чтобы сделать его ясным и легким для понимания, давайте рассмотрим пример: внутри вашего активити вам нужно реализовать 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
}
})

Как этого добиться? Просто вы просто создаете callback и класс, который расширяется до 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
}
}

И вот как теперь реализовать View:

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

Или, если вы используете Kotlin, можно использовать Extension Function:

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

И наконец, вот как реализовать в View:

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

Принцип инверсии зависимостей — DIP

Положитесь на абстракции. Не полагайтесь на конкреции.

Определение принципа инверсии зависимостей, данное дядей Бобом, состоит из двух пунктов:

  • Модули высокого уровня не должны зависеть от модулей низкого уровня. Оба должны зависеть от абстракций.
  • Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

Модули высокого уровня, которые обеспечивают сложную логику, должны легко использоваться повторно и не подвергаться изменениям в модулях низкого уровня, которые предоставляют служебные функции. Чтобы добиться этого, вам необходимо ввести абстракцию, которая отделяет модули высокого и низкого уровня друг от друга.

Простой пример — шаблон MVP. У вас есть объект интерфейса, который помогает взаимодействовать с конкретными классами. Это означает, что классам пользовательского интерфейса (Activity/Fragment) не нужно знать фактическую реализацию методов в Presenter. Таким образом, если у вас есть какие-либо изменения внутри Presenter, классам пользовательского интерфейса не нужно знать об изменениях или заботиться об этих изменениях.

Давайте посмотрим на это в этом примере кода:

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

Теперь посмотрим на это в UserActivity:

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()
}
//....
}
view raw UserActivity.kt hosted with ❤ by GitHub

Что мы делаем — создаем интерфейс, который абстрагирует реализацию презентатора, а наш класс представления сохраняет ссылку на PresenterInterface.

Что такое Чистый код и как его можно использовать в Android-разработке

Чистый код: вывод

Зрелые программисты знают, что представление о том, что все является объектом, является мифом. Иногда вам действительно нужны простые структуры данных с процедурами, оперирующими с ними. С этого момента вы должны начать думать о том, что реализовать, и о будущем, в котором чистый код можно будет легко обновить.

Я знаю, что вы раньше создавали приложения с бессмысленным именованием, классами Бога, спагетти-кодом — поверьте мне, я делал то же самое. Вот почему я делюсь с вами своими знаниями о Чистом коде от дяди Боба. И это также является напоминанием и для меня. Надеюсь, что смог помочь вам понять принципы.

Чистый код: что еще почитать

Если вы нашли опечатку - выделите ее и нажмите Ctrl + Enter! Для связи с нами вы можете использовать info@apptractor.ru.
Telegram

Популярное

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: