У меня есть обработчик событий в граничном классе, который управляет механизмом сохранения для данной универсальной транзакции:
void MyBoundaryClass::MyEventHandler(...)
{
//retrieve stuff from the UI
//...
//declare and initialize trasaction to persist
SimpleTransaction myTransaction(.../*pass down stuff*/);
//do some other checks
//...
//declare transaction persistor
TransactionPersistor myPersistor(myTransaction, .../*pass down connection to DB and other stuff*/);
//persist transaction
try
{
myPersistor.Persist();
}
catch(...)
{
//handle errors
}
}
Было бы лучше иметь какой-нибудь TransactionManager для обертывания объектов SimpleTransaction и TransactionPErsistor?
Есть ли какое-нибудь полезное практическое правило, чтобы понять, нужен ли мне дополнительный уровень инкапсуляции?
На данный момент я следую эмпирическому правилу: «Если метод становится слишком большим - сделайте что-нибудь с этим». Иногда бывает трудно найти правильный баланс между процедурным и объектно-ориентированным подходом при работе с обработчиками граничных событий.
Есть мнение?
Ваше здоровье


Учитывая, что:
Я бы сказал, что API является хорошим показателем актуальности новой высокоуровневой инкапсуляции (то есть определения нового объекта)
Если услуги (то есть API), предлагаемые этим новым объектом, согласованы и лучше доступны для остальной части программы при перегруппировке в один специальный объект, то непременно используйте новый объект.
В противном случае вероятно перебор.
Поскольку вы предоставляете общественный API, создавая новый объект, понятие тестовое задание может быть проще реализовать в этом новом объекте (и нескольких других объектах насмехаться), а не создавать множество устаревших объектов для тестирования тех же операций.
В вашем случае, если вы хотите протестировать транзакцию, вы должны фактически протестировать MyEventHandler из MyBoundaryClass, чтобы получить данные из пользовательского интерфейса.
Но если вы определите TransactionManager, это даст вам возможность меньшее связывание разных уровней архитектуры (GUI vs. data), присутствующего в MyBoundaryClass, и экспортировать управление данными в специальный класс. Затем вы можете протестировать устойчивость данных в независимом тестовом сценарии, уделяя особое внимание предельным значениям, сбоям базы данных, ненормальным условиям и т. д.
Сценарий тестирования может помочь вам уточнить сплоченность (важный момент, упомянутый Даок) ваших различных объектов. Если ваши тесты просты и логичны, скорее всего, ваши объекты имеют четко определенные границы обслуживания.
Поскольку можно утверждать, что Связь и сплоченность - два краеугольных камня объектно-ориентированного программирования., сплоченность нового класса, такого как TransactionManager, может быть оценена с точки зрения набора действий, которые он будет выполнять.
Cohesive means that a certain class performs a set of closely related actions. A lack of cohesion, on the other hand, means that a class is performing several unrelated tasks. [...] the application software will eventually become unmanageable as more and more behaviors become scattered and end up in wrong places.
Если вы перегруппируете поведения, которые иначе реализованы в нескольких разных местах в вашем TransactionManager, все будет в порядке, при условии, что его общедоступный API представляет четкие этапы того, что включает в себя транзакция, а не «вещи о транзакции», такие как различные служебные функции. Самого по себе имени недостаточно, чтобы судить о сплоченности класса. Требуется сочетание имени и его общедоступного API.
Например, одним интересным аспектом TransactionManager будет полная инкапсуляция понятия транзакции, которая:
Спасибо за ваше внимание. У меня уже есть TransactionPersistor - вы имеете в виду «Но если вы определите TransactionManager (...)»?
Действительно TransactionManager. Зафиксированный
Ура, один вопрос: я согласен с разделением, но, говоря о очень связанных классах, разве TransactionManager не является классическим образцом плохо связанного класса?
Я думал, что согласованность означает, что имя класса точно отражает ответственность класса (из «предварительного факторинга» О'Рейли) - в этом случае мы говорим, что общий «TransactionManager» выполняет / управляет вещами, «связанными» с транзакциями. .хорошее имя :-)
Имени недостаточно для определения сплоченности: имя и - необходим набор общедоступных функций API, чтобы увидеть, какие функции представляют собой «материал» о транзакциях (низкая сплоченность) или четкие шаги того, что включает в себя транзакция (высокая сплоченность )
Итак, если бы мой класс TransactionManager должен был предоставлять такие методы, как SetTransaction, GetTransaction, PersistTransaction и т. д., Это было бы очень связным, потому что имя + общедоступные методы четко определяют ответственность ...?
Да, за исключением части getTransaction. Можно было бы возразить, что некоторые операции / информация, которые вам могут понадобиться / запросить в транзакции, могут быть лучше инкапсулированы и ... управляются TransactionManager, что фактически усиливает его связность и снижает взаимосвязь между системой и транзакцией.
Вы предлагаете, чтобы TransactionManager полностью обернулся, чтобы создать транзакцию, поэтому нет необходимости в методах Set Get?
Это то, что я предлагаю в конце этого длинного ответа. Но вы решите, имеет ли это смысл в отношении остальной части вашей системы (которая действительно может нуждаться в самой транзакции). Если в этом нет необходимости, это отличный способ улучшить сцепление и снизить сцепление.
ваш ответ был действительно полезен - спасибо. Метод обработчика событий в объекте сократится, но меня беспокоит только то, что я могу получить больше строк кода в целом, если решу использовать TransactionManager.
Разрабатывая предложение VonC, примите во внимание следующие рекомендации:
Если вы ожидаете вызывать те же функции в другом месте таким же образом, разумно инкапсулировать их в новый объект.
Если одна функция (или один объект) предоставляет набор средств, которые полезны по отдельности, разумно реорганизовать ее на более мелкие компоненты.
Точка зрения VonC об API - отличная лакмусовая бумажка: создайте эффективный интерфейсы, и объекты часто становится очевидным.
И ... +1 за точность. Я дополню свой ответ
Уровень инкапсуляции должен быть напрямую связано со сплоченностью вашего объекта. Ваш объект должен выполнять одну задачу или должен быть разделен на несколько классов и инкапсулировать все его поведение и свойства.
Правило большого пальца - это когда пора протестировать ваш объект. Если вы выполняете модульное тестирование и понимаете, что тестируете несколько разных вещей (не в одной и той же области действия), вы можете попытаться разделить их.
Для тебя случай, я бы инкапсулировал вашу идею "TransactionManager". Таким образом, «TransactionManager» будет обрабатывать, как работает транзакция, а не «MyBoundaryClass».
не слишком ли название TransactionManager? Частично проблема связана с тем фактом, что если вы видите объект "менеджер", вы на самом деле не знаете, что он делает, поэтому связность моего объекта будет довольно плохой.
+1 за модные слова и теорию, подкрепленную практичностью и конкретным полезным предложением. Где вы были, ребята, до того, как я научился на собственном горьком опыте? :-)
вы сделали отличный замечание - могли бы вы объединить несколько соображений, применяя ваш общий комментарий к моему конкретному случаю / примеру?