Пятница, 2024-11-29, 6:28 PM
Главная страница Регистрация Вход
Приветствую Вас Гость | RSS
[ Новые сообщения · Участники · Правила форума · Поиск · RSS ]
  • Страница 1 из 1
  • 1
Методология программирования
radyonteamДата: Пятница, 2006-09-08, 2:51 AM | Сообщение # 1
Admin
Группа: Администраторы
Сообщений: 26
Репутация: 0
Статус: Offline
Программное обеспечение - это необычная среда для конструирования. Поскольку существует множество физических факторов, которые заставляют нас проектировать тем или иным образом, то большинство проектировочных решений не поддается объективному анализу. Как правило, речь о дизайне заходит не тогда, когда мы определяем, как работает программа, а тогда, когда мы хотим внести в нее изменения. Разумеется, работа программы очень важна, но все же о ее качестве, в первую очередь, говорит то, насколько просто внести в нее изменения. Именно стремление к изменчивости является причиной, по которой дизайн системы должен четко показывать, что и как она делает. В конце концов, довольно трудно внести изменения в программу, если ты даже не понимаешь, как она работает. Интересным следствием этого является случай, когда разработчики создают специальный легко изменяемый дизайн, который при этом затрудняет понимание кода. Тогда, естественно, эффект оказывается прямо противоположным.
Атрибуты и отображения
Допустим, нам нужна структура данных для описания человека. Мы можем реализовать ее, используя некоторые поля, как показано на рисунке 1. Разумеется, для этого нужно определить ряд переменных в классе Person. С другой стороны, язык Ruby, как и многие другие современные языки программирования, поддерживает отображения (известные также под именем ассоциативных массивов или хэш-таблиц). Мы могли бы определить структуру данных для класса Person, воспользовавшись отображением, как показано на рисунке 2 (это будет работать медленнее, но предположим, что производительность этой части кода не так уж важна).

Использование отображения - весьма привлекательный способ, так как он позволяет менять набор полей, сохраняемых в классе Person, не изменяя сам класс. Если вы захотите добавить сюда номер телефона, вам не придется менять для этого начальный код.

Несмотря на это, использование отображения не упрощает процесс модификации кода. Если я попытаюсь использовать эту структуру данных, я не смогу сразу определить, что в ней находится. Чтобы узнать, что некто использует ее для хранения количества материально зависимых лиц (numberOfDependents), мне потребуется прочесать всю систему. Если бы соответствующая переменная была определена в классе, то мне было бы достаточно одного взгляда на код класса, чтобы выяснить, какие поля он поддерживает.

Основной принцип ясного кода в том, что его проще понимать (и, как следствие, проще модифицировать). Как говорит Кент Бек, такой код представляет собой ясное выражение изначальных намерений.

Наш пример с отображением очень невелик, однако вышеописанный принцип остается верным при проектировании программных систем любого масштаба.

Рисунок 1. Использование полей класса (язык Ruby)
class Person
attr_accessor :lastName,:firstName,:numberOfDependents
end
def frag1
martin =Person.new
martin.firstName =“Martin ”
martin.lastName =“Fowler ”
martin.numberOfDependents =1
print (martin.firstName,““,martin.lastName,“has “,
martin.numberOfDependents,“dependents ”)
end

Рисунок 2. Использование отображения для хранения полей (язык Ruby)
class Person
attr_accessor :data
def initialize()
@data = {}
end
end

def frag2
martin = Person.new
martin.data [“firstName ”] = “Martin”
martin.data [“lastName ”] = “Fowler”
martin.data [“numberOfDependents ”] = 1
print (martin.data [“firstName ”],““,
martin.data [“lastName ”],“has “,
martin.data [“numberOfDependents ”],
“dependents ”)
end
События и явные вызовы
Вот еще один пример (несколько большего размера). Многие платформы поддерживают концепцию события как средства коммуникации между модулями. Предположим, у нас есть модуль обработки предварительных заказов, который при отмене заказа должен сделать так, чтобы модуль, отвечающий за работу с человеком, отправил оставившему заказ человеку соответствующее сообщение.

Мы можем реализовать это, использовав событие, как показано на рисунке 3. Для этого достаточно определить различные события для модуля предварительных заказов, и любой объект, который хочет выполнить некоторые действия при наступлении какого-либо события, может определить для него соответствующий обработчик. Этот подход выглядит привлекательным, так как нам не придется вносить изменения в класс Reservation, если мы захотим добавить какие-либо дополнительные действия при отмене предварительного заказа. Другие объекты тоже могут добавлять обработчики событий, поэтому вы сможете легко наращивать поведение в точках их обработки.

Рисунок 3. Отмена заказа с использованием событий (язык C#)
public delegate void ReservationHandler (IReservation source);
public class Reservation ...
public String Id;
public event ReservationHandler Cancelled;
public Person client {
get {
return client;
}
set {
value.AddReservation(this);
}
}
public void Cancel(){
Cancelled (this);
}

public class Person ...
public String EmailAddress;
public readonly ArrayList reservations;

public void SendCancellationMessage(Reservation arg){
//send a message
}

public void AddReservation(Reservation arg){
//invoke SendCancellationMessage when the cancelled event occurs on arg
arg.Cancelled +=
new ReservationHandler(SendCancellationMessage);
}

Рисунок 4. Явная реакция на отмену заказа (язык C#)
public class Reservation ...
public String Id;
public Person client;
public void Cancel(){
client.SendCancellationMessage(this);
}

Однако за использование событий приходится платить: просматривая код соответствующего метода, я не могу сказать, что происходит при наступлении какого-либо события. Чтобы разобраться в происходящем, придется просмотреть весь остальной разрозненный код, создающий обработчики для этого события. Более ясный и понятный код для изложенной задачи (рисунок 4) четко показывает последовательность действий при отмене заказа. Однако в этом случае при изменении или добавлении поведения при обработке события придется модифицировать код класса Reservation.

Я видел несколько примеров кода, в которых весьма интенсивно использовались события. И каждый раз в таких случаях было трудно понять, как ведет себя программа при вызове какого-либо метода. Особенно неудобно это при отладке, потому что некоторое поведение системы появляется вдруг там, где его совсем не ждешь.

Я не утверждаю, что события вообще нельзя использовать. Они позволяют наращивать поведение класса дополнительными действиями, не меняя сам класс. Это особенно удобно при работе с библиотечными классами, которые вы не можете изменить напрямую. События так же ценны тем, что не создают зависимости между классом, инициирующим наступление события, и классом, обрабатывающим его. Если эти классы находятся в различных пакетах, и вы не хотите делать пакеты зависимыми, отсутствие зависимости будет особенно полезно. Примером может послужить изменение содержимого окна на презентационном уровне при изменении объекта предметной области (модели). Механизм событий позволяет сделать это, сохранив исключительно важное разделение между моделью и ее презентацией.

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

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


Админ RadYonTeam
 
  • Страница 1 из 1
  • 1
Поиск:

 
Copyright MyCorp © 2006
Бесплатный хостинг uCoz