В качестве вступления хотелось бы вспомнить цитату У дяди Боба
Вы читаете эту статью по двум причинам.Представьте, что вы находитесь в библиотеке и ищете какие-то книги.Первое — ты программист, второе — ты хочешь быть лучшим программистом.
Если в библиотеке отсортирована и есть категории книг, то вы быстро найдете нужную.
Кроме того, классный дизайн интерьера и архитектура сделают пребывание в этой библиотеке для вас весьма комфортным.
Как и в случае с написанием книг, если вы хотите создать что-то великое, вам нужно знать, как писать и как организовывать свой код. Если у вас есть члены команды или кто-либо еще, у кого есть ваш (устаревший) код, им просто нужно увидеть имена переменных, пакеты или классы, и они сразу поймут. Им не нужно говорить «К черту» этот код и начинать все заново, с нуля.
Что такое чистый код?
Перевод Дорогой программист: Когда я писал этот код, только Бог и я знали, как он работает! Теперь это знает только Бог! Прежде чем вы попытаетесь оптимизировать эту процедуру, и это не удастся (скорее всего), увеличьте счетчик затраченного времени в качестве предупреждения следующему разработчику.
Всего потрачено часов: 567 Как видите, недостаточно просто закончить разработку быстрее.
Если в будущем они не смогут понять этот код, то это тоже станет техническим долгом.
Ваш код будет «чистым», если каждый член вашей команды сможет его понять.
Чистый код может быть прочитан и улучшен любым разработчиком, кроме оригинального автора.
С пониманием приходит читаемость, изменчивость, расширяемость и ремонтопригодность.
Должен ли я беспокоиться об этом?
Причина, по которой вам следует заботиться о чистоте вашего кода, заключается в том, что вы передаете свои мысли другим разработчикам.Вот почему вам следует позаботиться о том, чтобы ваш код был более элегантным, простым и читабельным.
Признаки чистого кода
- Ваш код должен быть элегантным.
Ваш код должен вызывать у вас улыбку, как хорошо сделанная музыкальная шкатулка или хорошо спроектированный автомобиль.
- Ваш код должен быть целенаправленным: каждая функция, каждый класс, каждый модуль реализует одну задачу, которая остается совершенно не отвлекающейся и незагрязненной окружающими деталями.
- Код не содержит дубликатов
- Пройдите все тесты успешно
- Количество сущностей, методов, классов и т. д. было сведено к минимуму.
Разница между хорошим программистом и профессиональным в том, что профессиональный программист понимает, что ясность кода имеет первостепенное значение.Профессионал использует эту силу, чтобы писать код, понятный каждому - Роберт К.
Мартин
Пишите осознанные имена
Выбор хорошего имени может занять много времени, но в долгосрочной перспективе это сэкономит вам больше.Имя переменной, функции или класса должно отвечать на все соответствующие вопросы.
Он должен рассказать вам, почему он существует, зачем он нужен и как его использовать.
Если имя требует комментария, то оно не раскрывает своего назначения.
Простой пример
// 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()
Имена классов
Классы и объекты должны быть существительными, например Customer, WikiPage, Account и AddressParser. Избегайте таких слов, как «Менеджер», «Процессор», «Данные» или «Информация».Помните также, что имя класса не должно быть глаголом.
Имена методов
Имена методов должны быть глаголами, например postPayment, deletePage или save. Модификаторы доступа и предикаты должны быть названы в соответствии с их значением и иметь префикс get, set и в соответствии со стандартом JavaBean. Прежде чем продолжить, сделайте небольшой перерыв, запаситесь кофе и печеньем.
Хорошо, теперь перейдем к принципам SOLID.
Пишите код, используя принципы SOLID.
Эти принципы были разработаны дядей Бобом.SOLID — это аббревиатура, описывающая набор принципов, предназначенных для написания хорошего кода.
Принцип единоличной ответственности (S)
Это означает, что каждый класс должен иметь только одну ответственность.Никогда не должно быть более одной причины для изменения класса.
Вам не обязательно добавлять в свой класс все только потому, что вы можете.
Разбивайте большие классы на более мелкие и избегайте классов Бога.
Пример:
У нас есть 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)
}
}
Это освобождает RecyclerView.Adapter от единоличной ответственности, поскольку он содержит бизнес-логику внутри onBindViewHolder. Этот метод отвечает только за вставку данных в представление.
Принцип открытия/закрытия (O)
Программные объекты должны быть открыты для расширения, но закрыты для модификации.Это означает, что если вы разрабатываете класс А и ваши коллеги хотят изменить функцию внутри этого класса.
Они могут легко сделать это, расширив этот класс, не меняя сам класс.
Простым примером является класс 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 и передаете ему действие Toast при вызове onClick().
interface ClickListener {
fun onClick()
}
class MyActivity: AppCompatActivity(), ClickListener {
//.
override fun onClick() {
// Do the magic here
toast("OK button clicked")
}
}
Принцип разделения интерфейсов
Этот принцип гласит, что клиент не должен зависеть от методов, которые он не использует. Это означает, что если вы хотите написать класс 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
}
})
как нам это сделать? Легко! Давайте просто создадим обратный вызов и класс, расширяющий 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)
Или вот так, используя функцию расширения в Котлине.
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)
Принцип инверсии зависимостей
Зависимость от абстракций, без зависимости от чего-то конкретного.Определение инверсии зависимостей дяди Боба состоит из двух понятий.
Модули более высоких уровней не должны зависеть от модулей более низких уровней.
Оба должны быть привязаны к абстракциям.
Абстракции не должны зависеть от деталей.
Детали должны зависеть от абстракций.
Модули высокого уровня, реализующие сложную логику, должны легко использоваться повторно без внесения изменений в модули более низкого уровня.
Для этого нужно ввести абстракцию, отделяющую модули верхнего и нижнего уровня друг от друга.
Простой пример этого шаблона MVP. У вас есть объект интерфейсов, который помогает вам взаимодействовать с конкретными классами.
Это означает, что классам пользовательского интерфейса (Activity/Fragment) не обязательно знать фактическую реализацию методов презентатора.
Таким образом, если вы вносите изменения внутри презентатора, классы пользовательского интерфейса не должны заботиться об этих изменениях.
Давайте посмотрим на пример 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.
Теги: #Android #Разработка Android #Разработка мобильных приложений #программирование #Kotlin #дизайн и рефакторинг #solid #рефакторинг #чистый код
-
Руководство По Устранению Неполадок Печати
19 Oct, 24 -
Почему Все Любят Assassin’s Creed
19 Oct, 24 -
Онлайн-Встреча По Системному Анализу, 08/09
19 Oct, 24 -
Переполнение Таблицы
19 Oct, 24 -
100 Самых Странных Сайтов В Интернете
19 Oct, 24