Для начала, я думаю, этот вопрос лучше задать в Softwareengineering . Тем не менее, я мог бы также дать вам несколько моих центов на это.
Как часто, это зависит ...
Как правило, инкапсуляция является одним из основных понятий в объектно-ориентированном программировании.
Любое изменение состояния объекта должно быть сделано самим объектом (хотя оно может быть вызвано извне) и поэтому должно гарантированно соответствовать условиям, которые вы определили для своего объекта. Поведение вашего объекта должно быть реализовано внутри вашего объекта, а не за его пределами.
Вы не хотите публично раскрывать свой атрибут Window
wall
для всего мира, чтобы получить к нему прямой доступ. Вы хотите скрыть это за добытчиками и сеттерами. Вы хотите, чтобы Window
отказывался помещаться в Wall
, который передается его установщику wall
, если упомянутое Wall
оказывается «внутренним». Вы не хотите, чтобы объект Person
изменял Window
s state
с 'open' на 'close' и наоборот напрямую, вы хотите, чтобы Person
вызывал Window
's open()
соответственно close()
метод, например внутренне убедиться, что закрытое окно больше не закрывается.
Кроме того, скрытие деталей реализации может помочь поддерживать ваш интерфейс и сделать прозрачными изменения в вашем классе. Скажем, например, вы решили, что, помимо запрета внутренних стен, теперь вы также хотите не допустить установки «обычных» окон в наружные стены в подвале. Вы можете реализовать эту проверку в своем существующем установщике wall
в Window
, и единственным видимым изменением для внешнего кода будет еще одна потенциальная причина отказа («окно = нормальное и стена = подвал» в дополнение к «стене = внутреннее пространство») , Или вы хотите добавить атрибут, представляющий состояние чистоты вашего Window
, и, чтобы провести надлежащее различие между новым cleanliness_state
и старым 'open' / 'close' state
, вы хотите переименовать старый атрибут open_close_state
. С вашими методами open()
, close()
(и, возможно, is_open()
и is_closed()
), считывающими и записывающими в ваш атрибут 'open' / 'close' state
, это изменение влияет только на реализацию вашего класса, а не на каждую часть кода, который его использует.
Однако!
Возможно, у вас есть классы, которые просто работают как некая коллекция, т.е. data classes . Они практически не реализуют функциональность и публично предоставляют свои атрибуты для чтения и записи всему миру, таким образом широко игнорируя концепцию инкапсуляции. Можно утверждать, что классы / модели, реализованные на уровне объектно-реляционного отображения, таком как SQLAlchemy, являются скорее объектом данных / классом данных, чем объектом в смысле ООП, особенно когда они используются в основном для сохранения и извлечения структурированных данных. Нет ничего необычного в том, что внешний код изменяет состояние такого объекта или реализует его функциональные возможности, такие как представления в структуре Django, которая использует свой собственный уровень ORM для реализации и сохранения моделей.
Так
Это сводится к вашему конкретному случаю. Вы уже упоминали, что рассматриваете возможность ограничения размещения окон; вероятно основанный на свойствах окон и вовлеченных стен.
Если вы рассматриваете свои модели SQLAlchemy больше, чем просто способ сохранения ваших объектов, продолжайте внедрять поведение и изменять логику прямо в вашей модели. Но имейте в виду, что а) вы можете в конечном итоге создать конфликты с методами / свойствами базового класса вашей модели и б) атрибуты ваших моделей должны оставаться открытыми для поддержания функциональности вашего уровня ORM (хотя SQLAlchemy может работать со свойствами до тех пор, пока оба, getter и setter определены; я никогда не проверял это).
Если вы хотите, чтобы модели были довольно удобным методом сохранения и извлечения ваших структурированных данных, сохраняйте их чистыми и используйте некоторые служебные функции или классы, которые реализуют поведение вашего объекта и обеспечивают его контракт при использовании в коде; например есть функция place_window_on_wall(window: Window, wall: Wall)
, которая заботится о проверке и ограничениях, когда вы пытаетесь сослаться на объект Wall
на ваш атрибут Window
wall
. Но имейте в виду, что изменения в вашей модели должны отражаться и в этих функциях / классах.
Я считаю оба варианта действительными; все, что вы выберете, будет соответствовать вашему решению.