Может понадобится, например, когда нужно перенести какие то данные из одной базы данных, в базу данных под управлением django. В таком случае, по первой базе данных нет необходимости (или невозможно) формировать модели.
При необходимости, к первой базе тоже можно применить модели в django приложении.
Первым делом, добавляем второе подключение («sourcedb«), наряду с основным («default«) в файле конфигов settings.py. Для примера, у нас mysql:
Теперь можно использовать второе подключение для забора данных:
1) одну запись (fetchone):
from django.db import connections
…
def get_single_record_from_second_database(order_number):
try:
with connections['sourcedb'].cursor() as cursor:
cursor.execute("""SELECT id, `sum`, user_id
FROM orders
WHERE order_number = '%s'
""" % order_number)
order = cursor.fetchone()
cursor.close()
except Exception as error:
print("Failed to read data from Orders table (for %s):" % order_number, error)
...
2) или обрабатываем много записей (fetchall)
from django.db import connections
…
def get_multiple_records_from_second_database(date):
try:
with connections['sourcedb'].cursor() as cursor:
cursor.execute("""SELECT id, `sum`, user_id
FROM orders
WHERE date = '%s'
""" % date)
orders = cursor.fetchall()
for order in orders:
...
cursor.close()
except Exception as error:
print("Failed to read data from Orders table (for %s):" % date, error)
...
В цикле for можно обрабатывать заказы (order) и заполнять данными основную (default) базу данных
Это руководство для новичков в Django, которые прочитали начальное руководство по Django на официальном сайте и столкнулись с задачей загрузки csv файла с таблицей в базу данных
В этом случае, у вас есть приложение polls, с урлами, моделями, вьюшками для классов вопросов (Question) и ответов (Choices). И база данных с сгенерированными таблицами (polls_question, polls_choice)
Для примера, будем загружать в базу данных csv файл с вопросами (Questions).
Назовем файл questions_01a.csv и поместим туда такое содержимое:
Why?,2022-11-14 10:00
For what?,2022-11-14 10:01
When?,2022-11-14 10:02
Where?,2022-11-14 10:03
Перенос строк — как разделитель информации по вопросам, и запятая — как разделитель ячеек данных
Изменения в /polls/urls.py:
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
…
path('upload_csv/', views.upload_csv, name='upload_csv'),
]
Изменения в /polls/views.py:
from django.http import HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.urls import reverse
from django.views import generic
from django.utils import timezone
from django.contrib import messages
from .models import Question, Choice
…
def upload_csv(request):
data = {}
if "GET" == request.method:
return render(request, "polls/upload.html", data)
# if not GET, then proceed
try:
csv_file = request.FILES["csv_file"]
if not csv_file.name.endswith('.csv'):
messages.error(request, 'File is not a CSV')
return HttpResponseRedirect(reverse("polls:upload_csv"))
# if file is too large - error
if csv_file.multiple_chunks():
messages.error(request, "Uploaded file is too big (%.2f MB). " % (csv_file.size/(1000*1000),))
return HttpResponseRedirect(reverse("polls:upload_csv"))
file_data = csv_file.read().decode("utf-8")
lines = file_data.split("\n")
# loop over the lines and save them to db via model
for line in lines:
fields = line.split(",")
try:
question = Question(
question_text=fields[0],
pub_date=fields[1],
)
question.save()
except Exception as e:
messages.error(request, "Unable to upload file. "+repr(e))
pass
except Exception as e:
messages.error(request, "Unable to upload file. "+repr(e))
return HttpResponseRedirect(reverse("polls:upload_csv"))
Что делаем в views.py:
Импортируем стандартный фреймворк сообщений (messages), чтобы можно было на страничке выводить ошибки
Файл будем посылать методом POST, если же запрос GET — открываем страницу аплода
Делаем простые проверки получаемого файла (тип файла и максимальный размер)
Читаем построчно и сохраняем в базу данных, через модель
Создаем страничку для загрузки файла — /polls/templates/polls/upload.html со следующим кодом:
Одна из распространённых причин медленных запросов — обработка лишних данных
Это может быть запрос лишних строк из большой таблицы, где не все они нужны. Можно использовать LIMIT или ограничивать выборку WHERE
SELECT * FROM huge_table LIMIT 10;
-- или
SELECT * FROM huge_table WHERE id BETWEEN 0 AND 1000;
Это может быть запрос всех столбцов в таблице с большим их количеством или в нескольких таблицах, соединенных JOIN. Лучше, запрашивать только необходимые столбцы
SELECT id, name, email FROM users LIMIT 1000;
Это может быть повторный запрос за теми же данными. В этом случае, если не получается избежать, стоит кешировать результаты и использовать данные из кеша
Другая причина медленных запрсов — анализ лишних данных
Необходимо минимизировать этот параметр. Например, запрос по индексируемому столбцу анализирует очень ограниченный набор данных, а по этому же столбцу без индекса — может анализировать все строки таблицы. Простейшее решение будет добавление или оптимизация индексов (ссылка на статью)
Каким образом и сколько данных анализируется, можно посмотреть используя команду EXPLAIN и строка rows в результатах
Эффективным способом ускорения сложного запроса может быть разбиение его на более простые, которые выполняются быстрее первоначального
-- Было
SELECT * FROM tag
JOIN tag_post ON tag_post.tag_id = tag.id
JOIN post ON tag_post.post_id = post.id
WHERE tag.tag_name = ‘mysql’;
-- Стало
SELECT * FROM tag WHERE tag_name = ‘mysql’;
SELECT * FROM tag_post WHERE tag_id = 1234;
SELECT * FROM post WHERE id IN (123, 456,789);
Или разбиение очень длительного запроса на более короткие порции
-- Было
DELETE FROM messages WHER created < DATE_SUB(NOW(), INTERVAL 3 MONTH);
-- Стало
DELETE FROM messages WHER created < DATE_SUB(NOW(), INTERVAL 3 MONTH) LIMIT 1000;
-- несколько раз
Оптимизатор MySQL плохо оптимизирует корректированные подзапрос вида
SELECT * FROM films WHERE id IN (
SELECT film_id FROM film_actors WHERE actor_id = 100
);
В этом случае, лучше разделить и связать их на бекенде
SELECT film_id FROM film_actors WHERE actor_id = 100;
-- И затем
SELECT * FROM films WHERE id IN (1,5,9,13,77);
Не стоит злоупотреблять сортировкой ORDER BY, так как, без индекса на сортируемом столбце, она может значительно увеличить время выполнения запроса. Можно использовать LIMIT для ускорения сортировки, если она необходима
Запросы с UNION часто идет с ограничением LIMIT, и для ускорения, следует добавить LIMIT в оба под-запроса
-- Было
(SELECT id, name FROM film_actors order by name)
UNION ALL
(SELECT id, name FROM theatre_actors order by name)
LIMIT 20;
-- Стало
(SELECT id, name FROM film_actors order by name LIMIT 20)
UNION ALL
(SELECT id, name FROM theate_actors order by name LIMIT 20)
LIMIT 20;
Запросы с LIMIT и OFFSET на больших таблицах плохо работают с большими OFFSET, а это часто встречается в пагинации
Например для:
SELECT * FROM users ORDER BY id LIMIT 100 OFFSET 40000;
Сервер сгенерирует 40100 строк и отбросит первые 40000
Стоит заменить на:
SELECT * FROM users WHERE id > 40000 ORDER BY id LIMIT 100;
Ну и на последок, на бекенде часто используется ORM для генерации запросов. И нужно понимать, что для сложных запросов ORM формирует неоптимальный медленный запрос. В таком случае, следует переписать ORM запрос на чистый SQL
Самое актуальное издание книги — третье. Опубликовано автором в 2013м году. Почти десять лет прошло.
Считается, что книгу нужно прочитать одной из первых специалистам по Пользовательскому опыту на веб сайтах (UX-специалисты). Материал в книге подается просто и понятно. Специалистов по пользовательскому опыту на веб-сайтах пока еще мало и не каждая компания имеет такого. Поэтому стоит ознакомиться с книгой и разработчикам. Тем более что объем книги небольшой.
Некоторые вещи из книги кажутся очевидными, но ценность в акценте на них как на важных. Основной посыл книги заложен в названии — «Не заставляйте пользователя думать». Идея кажется простой, но реализовать ее сложнее.
Мысли, которые почерпнул для себя из книги Стива Круга:
Со временем эволюции веба, у пользователей уже сложились представления о том, как должен выглядеть сайт. Где находится логотип, где меню, где разделы, где основная информация. Лучше размещать эти разделы так и там где пользователь привык их видеть. Не стоит удивлять пользователя и заставлять его искать привычные вещи.
Привычные названия для действий — это хорошо (кнопка «Регистрация»). Не привычные, остроумные, вычурные — плохо (вместо «Войти» — кнопка «Вперед за впечатлениями!»), так как заставляет пользователя задуматься и потерять время.
Основная навигация сайта (лого, слоган, меню, футер) и сервисные функции (войти, зарегистрироваться, поиск) должны быть на каждой странице и выглядит одинаково
Пользователи проводят на нашем сайте гораздо меньше времени, чем мы думаем. Редко они читают заготовленный текст полностью. Только если это не статья по интересующей его теме или «рецепт ребрышек в духовке». Пользователи хватают информацию кусочками: пару слов из целого предложения; первое предложение из абзаца.
Текст необходимо форматировать так, чтобы его было удобно просматривать («просматривать» отличается от «читать»). Иерархия заголовков. Короткие абзацы. Маркирование списки. Выделенные ключевые слова или понятия
Эффективная визуальная иерархия. Чем важнее элемент, тем он должен быть заметнее. Визуально связываем логически-связаные элементы. Часть чего-то большего оформляем вложенностью в это «большее»
Логические области (лого, меню, контент, баннеры, навигация, футер) должны быть четко разграничены, чтобы облегчить пользователю ориентацию на сайте
Кликабельные области (кнопки, ссылки, табы) должны быть выделены, привычным образом. Чтобы не было сомнения, что это кнопка или ссылка
Логотип сайта находится слева сверху. Рядом — слоган, которые четко объясняет куда мы попали и чего ожидать от сайта («Лучшие свежие торты Санкт-Петербурга»)
Хлебные крошки на сайте — это хороший тон. Если иерархия страниц больше 2х уровней.
Обязательно включаем поиск по сайту. Некоторые пользователи сразу же приступают к поиску, а не навигированию через меню
Пункты навигационного меню должны быть лаконичными, но понятными. Включать в свои разделы точно идентифицируемые области, а не пересекающиеся. Чтобы у пользователя не возникало вопроса «Бензопила» находится в разделе «Домашнее хозяйство» или «Для дачи»
На главной странице сделать акцент на то, от куда начать пользователю. Это или Регистрация или Покупка или Чтение лучших статей.
В сражении за место на главной странице между отделами (маркетинг, сео, продакт, заказчик, директор, ux-дизайнер и другие) необходимо, чтобы ответы на 4 основных вопроса легко находились.
Что это за сайт?
Что я могу здесь делать?
Почему я должен остаться здесь, а не где-либо еще?
Какие материалы здесь есть?
Главная страница все еще важна. Даже если на ваш сайт попадают из поисковика и не на главную.
Юзабилити-тестирование очень полезная технология. Можно получить много идей для улучшения веб-сайта, пригласив сторонних людей попробовать с ним работать
Когда мы говорим про оптимизацию запросов, в первую очередь, стоит оптимизировать сами типы столбцов таблицы. Лучше это делать при создании таблиц или при добавлении новых столбцов в таблицу.
Меньшие по размеру типы данных обычно быстрее, так как занимают меньше места на диске, в памяти и кэше процессора
Следует упрощать сложные типы данных, если такое возможно.
Например, IP адреса лучше хранить в целочисленных данных, так как сравнение строковых данных работает намного медленнее.
Даты и время рекомендуется хранить во встроенных типах данных MySQL. Из DATETIME и TIMESTAMP, последний предпочтительнее, так как требует вдвое меньше места для хранения
Целые числа: TINYINT (8 бит), SMALLINT (16 бит), MEDIUMINT (24 бит), INT (32 бит), BIGINT(64 бит) и диапазоны значений от -2^(N — 1) до 2^(N — 1). В зависимости от планируемого диапазона значений — выбираем более простой тип
Часто, отрицательные значений не требуются (например, авто-инкрементирующийся ID) и можно расширить диапазон в два раза благодаря атрибуту UNSIGNED.
Вещественные числа занимают значительно больше места.
Расчеты с данными с плавающей точкой высокозатратные и могут приводить к ошибкам на бекенде, когда «1» на самом деле берется как «0.9999987». В том числе поэтому, целесообразнее выбрать Целочисленный тип вместо Вещественного. Если нам известно, что у данных максимум 2 знака после запятой, «109.40» храним и обрабатываем как «10940»
Наиболее часто используемый строковый тип — VARCHAR. Используется при хранении данных переменной длины.
В отличии от VARCHAR, CHAR имеет фиксированную длину и лучше оптимизирован для сравнения строк с примерно одинаковой длинной (например для хранения MD5 сверток данных пользователя).
CHAR так же эффективен для хранения часто изменяемых данных, так как не подвержен фрагментации при записи
Для хранения больших данных используются: TEXT — символьные данные, BLOB — двоичные данные
Если количество вариаций данных ограничено, то можно использовать ENUM.
ENUM работает быстро, так как, по сути, хранит целое число. Недостатком ENUM будет то, что при добавление нового значения в набор, нужно будет менять схему таблицы (ALTER TABLE) или писать миграцию на бекенде
Лучше избегать значений NULL, если они не нужны.
Стоит объявлять столбец как NOT NULL. MySQL тяжелее оптимизировать запросы к столбцам, допускающим NULL
Индексы (или ключи) — это специальные структуры данных, которые использует подсистемы хранения для ускорения нахождения строк.
Индексы бесполезны, и, даже, вредны, если данных в таблицы мало (тысячи или меньше строк)
Если данных много (десятки тысяч строк и больше) и есть запрос WHERE по одному столбцу (или же сортировка ORDER BY по этому столбцу), который выполняется часто, лучше «повесить» на него простой индекс
Индекс хорошо работают по полному значению, диапазону значений
Составной индекс включает набор индексов (например, по трем столбцам A + B + C)
В это случае, индекс хорошо работает по полному набору проиндексированных столбцов («A + B + C»), по префиксам (левой части индекса: «A» или «A + B») и НЕ работает по пост-фиксам (правой части индекса: «C» или «B + C»). В случае необходимости поиска по «B» или «B + C», можно создать отдельный индекс — «B + C». Опять таки, индекс «B + C» будет неэффективен при поиске по C.
Альтернативой при существующем индексе «A + B + C» и необходимости поиска по пост-фиксу индекса, может быть включение в поиск столбца A искусственным образом:
SELECT * FROM TABLE WHERE B=2 AND C=3;
-- преобразуем в:
SELECT * FROM TABLE WHERE A="2022-10-14" AND B=2 AND C=3;
Так же, необходимо рассмотреть возможность смены порядка следования столбцов в запросе WHERE. Если, имеем индекс «A + B + C», при этом наш запрос выглядит так:
-- было:
SELECT * FROM TABLE WHERE A = 1 AND C = 2 AND B = 3;
-- стало:
SELECT * FROM TABLE WHERE A = 1 AND B = 3 AND C = 2;
Нужно понимать, что, если в запросе есть поиск по одному столбцу и сортировка по другому, хорошо подойдет составно индекс по этим столбцам и плохо будет работать отдельный индекс по одному из столбцов
В случае индексов на столбец строкового типа, индекс хорошо работает по полному совпадению и при поиске по префиксу (WHERE column LIKE «abc%»). При это у индекса есть ограничения, и по длинной строке поиск будет работать плохо
Чем меньше памяти потребляет тип столбца, тем быстрее работает и индекс. Поэтому поиск по индексированному целочисленному столбцу гораздо быстрее, чем по строковому
В случае длинных строк, и необходимости поиска по ним, можно ввести индексируемый столбец с хешем по длинной строке.
Хороший пример данных, это столбец с адресом веб-страниц url и поддержка столбца c хешем этой строки — url_hash. При сохранение данных, записываем в столбец url оригинальный адрес веб-страницы, а в url_hash — хеш строки этого адреса (например кодировкой CRC32 или другой кодировкой в цифровое значение). А при поиске ведем поиск только по индексированному url_hash.
-- долгий запрос:
SELECT * FROM TABLE WHERE url = "https://long-adress-url.com";
-- заменяем на:
SELECT * FROM TABLE WHERE url_hash = CRC32("https://long-adress-url.com");
Другой частый случай плохо-индексируемых данных — это IP-адреса. Для них, так же есть специальный функции на бекенде, которые превращают строку IP-адреса в числовой тип для отдельного индексируемого столбца.
Конкретные функции зависят от языка программирования. Например, для PHP подойдет функция ip2long для шифровки и long2ip для расшифровки
Книга помогает сформироваться лидеру группы. В мире программирования, лидер — это технический директор, тим-лид, сеньор в команде разработки.
Отвечает на множество вопросов по работе лидеров групп
Приведены примеры реальных ситуаций-вызовов. Какие решения предлагали лидеры известных компаний
Недостатки книги для меня, как айтишника:
Многие примеры ориентированы на “продажников” или менеджеров компаний, управляющих ими
Текст рассчитан больше на американские или западно-европейские реалии
Но это не мешает понять главное в отношении менеджеров и сотрудников
Моменты, которые я выделил бы для себя:
Программисты считают себя интеллектуальной элитой и не признают авторитетов. К ним нужен особый подход
Формальная должность менеджера работает плохо. Чтобы лидерская позиция работала эффективно, надо чтобы команда признала лидера и неформально. В этой книге много советов в этой области
Вам должны доверять. В вас должны видеть советника. А чтобы это произошло, нужно помогать своим подопечным. Делать это корректно и терпеливо. При этом не навязывать свою помощь. Помощь будет удачной, только если она своевременна.
Подчиненные должны нравиться лидеру группы. Если это не получается сразу, необходимо найти черты или способности, которые помогут. Это важный критерий при наборе на работу новых людей
Интересоваться жизнью коллег и делиться своей. Строить длительные доверительные отношения. Общаться и за пределами работы
Помогать людям понять свои перспективы и мечты, двигаться к ним
При этом быть “рядом” в коменты личных или профессиональных кризисов
Сотрудники могут допускать ошибки. Они не должны испытывать затруднения, когда рассказывают о своих упущениях. Это достигается созданием безопасной атмосферы в группе
Действовать в интересах не только компании, но и своих сотрудников
Нововведения в группу намного лучше интегрируются если к ним приходят совместно группой
Одобрение — один из самых эффективных форм поощрения. Оно должно быть своевременным, искренним
Наставничество можно описать моделью:
Спросите как дела
Убедитесь, что человек готов принять наставничество
Задайте вопросы и предложите свою поддержку
Поделитесь нужной информацией
Активно слушайте
Помогите найти возможные направления действия
Договоритесь о следующем шаге
Модель Помощи отстающим коллегам:
Организуем встречу для обсуждения проблем с производительностью
Убеждаем сотрудника, чтобы в него верите и хотите оказать поддержку
Добиваемся того, что проблема существует и обсуждаем причины
Находим идеи для улучшения
Договариваемся о конкретных действиях для решения проблемы
Устанавливаем конкретную дату для обсуждения того, как идет исправление
Модель противодействия Проблемномуповедению:
Организовываем встречу
Описываем конкретные ситуации, иллюстрирующие проблемное поведение
Объясняем, почему такое поведение беспокоит вас, и выражаем желание изменить его
Выслушиваем объяснение коллеги по поводу причин происходящего
Помогаем увидеть, как изменение поведения поможет карьере сотрудника
Спрашиваем, какие у него есть идеи по поводу решения проблемы
Предлагаем свою поддержку и помощь
Договариваемся о плане действий и назначен дату для обсуждения хода дел
Среда разработки помогает быстрее писать код. А еще, она подсказывает как сделать код чище. Стоит обращать внимание на все предупреждения среды разработки и учитывать их. Так же выявляется и дублирование кода
Дублирование — большая помеха чистоте кода. Простейшая форма дублирование — целые куски дублирующего кода. Их стоит выделять в отдельные функции, иногда — классы и пере-использовать. Более сложные примеры дублирования проявляются в одинаковых условиях if-else. Вместо них следует использовать принцип полиморфизма. Существуют еще более сложные случаи дублирования. Схожие алгоритмы могут соседствовать со специфичной логикой. Используем паттерны проектирования, например Шаблонный метод или Стратегия.
Дублирующий код может быть инкапсулирован в более высокие уровни абстракций, если необходимо пере-использовать его в других классах.
Не используемый код следует удалять, а не сохранять на будущее. Тоже происходит с “мертвым кодом”, до которого алгоритм никогда не дойдет. Это нередкая “болезнь” циклов if-else и switch-case
Вертикальное разделение помогает чтению кода. Переносы строк, без пропуска строк, покажут что код относится к одной логике. Пропуски строк разделят алгоритм на 2е логически-обособленные части
Такой балласт, как пустой конструктор или пустое тело цикла будет мешать читать код. Следует удалить их.
Пояснительные переменные помогают чтению и пониманию кода
Чистоте кода, так же, поможет соблюдение стандартных конвенций. Не обязательно это должен быть согласованный документ. Сам код служит примером оформления.
Так называемые “волшебные числа” заменяют именованными константами — пример кода со стр 339
Комментарии к коду — не однозначная концепция. С одной стороны, комментарии используют для пояснения работы следующего кода. С другой стороны, одна из концепций “Чистого кода”: код должен быть понятным и без комментариев.
От сюда, обозначим основные концепции:
Комментарии быстро устаревают. Важно не забывать обновлять комментарии при обновлении следующего кода. Это известная проблема комментариев. Устаревшие комментарии могут быть вредны. Они описывают одно поведение, а алгоритм кода — другое, что путает программиста.
Комментарии увеличивают размер кодовой базы и время на изучение кода. Нужно прочитать не только код, но и комментарии, чтобы вникнуть в алгоритм.
Есть такое заблуждение, что комментарии компенсируют плохой код. Например, пишем не очень понятный или запутанный код, а далее, в комментариях поясняем его работу. Лучше написать ясный и выразительный код, чтобы комментарии не потребовались
Замена комментариям — подходящие, говорящие имена переменных, методов и классов
Лучше удалять старый код, чем комментировать его. Закомментированный код со временем разрастается и создает большой объем бесполезного или вредного груза.
В некоторых случаях комментарии уместны. Они будут мощным инструментом.
Уместные типы комментариев:
Юридические комментарии в начале модулей или классов, копирайты
Пояснения к сложным регулярным выражениям или маскам
Если используются какие то стандартные имена из сторонних библиотек. При этом, мы не можем на них повлиять, но можем описать комментариями
Когда передают опыт использования или предупреждения (например, // код ниже может работать очень долго если количество записей в базе данных больше миллиона)
Комментарии TODO
Усиление значения какого то факта, если его значение может показаться неочевидным (например, //не удаляйте код ниже, так как это защита от…)
Комментарии вида APIdoc, необходимые для генерации авто-документации по методам АПИ
Классы и функции — важнейшие блоки построение кода проекта. Это делает их основными целями при работе с чистотой кода.
Рекомендации к написанию функций:
Функции должны быть как можно компактнее. Можно ориентироваться на размер максимум 20 строк, чтобы понимать масштабы
Функции могут содержать вложенности (циклы foreach, if else, while). Следует уменьшить число вложенностей до 1-2. В идеале одна вложенность. Больший уровень вложенности можно помещать в отдельные функции
Функция должна выполнять одну операцию. Это упрощает разделения алгоритма на функции
Если в функции есть “секции” (например объявление, инициализация, сбор данных и т.д.) значит она выполняет несколько операций. Следует разделить на разные функции
При этом имя функции следует выбирать таким образом, чтобы доходчиво объяснить операцию
Связанные функции следует размещать ближе к “родительским”. Создаем, таким образом, понятный уровень иерархии, который читается сверху вниз
Следует минимизировать количество аргументов функций. Большое количество аргументов усложняет понимание функции. Лучше всего читаются функции без аргументов. Они могут манипулировать полями классов ($this->variable).
Если в функцию нужно передавать много аргументов, следует рассмотреть объединение их в класс, тогда из аргументов будет только один объект класса
Классы:
Оптимальный вариант строения класса: сначала идут открытые переменные, затем приватные. За списком переменных идут публичные функции. За каждой публичной функцией следуют приватные вспомогательные функции. При таком размещении, класс читается как газетная статья
Классы должны быть компактными. Размер класса может определить принцип единой ответственности. Этот принцип говорит, что класс должен иметь только одну причину для изменения. Пример класса с 2мя причинами для изменения ниже
Class UserDashboard {
public function getLastFocusedElement() {…};
public function setLastFocusedElement() {…};
public function getMajorVersion() {…};
public function getMinorVersion() {…};
}
Его стоит разделить на 2 класса: первый — для работы с выделенным элементом дашборда, второй — для версионирования дашборда
Следует стремиться к бОльшей связанности в классах. БОльшая связанность достигается, когда бОльшая часть методов класса использует бОльшую часть переменных экземпляра класса. Разделение большого метода на серию более мелких снижает связанность в классе. После этого становится понятнее как разбить исходный класс на более компактные. В результате, повышается связанность и чистота классов, полученных из исходного класса.