Xpoint
   [напомнить пароль]

ООП, связаные классы и повторное использование кода

Метки: [без меток]
2006-12-10 20:23:27 [обр] hdd[досье]

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

Простой пример:


A - класс БД, который использует какой-нить более низкоуровневый класс (B, для ведения логов, например), классы C, D, E используют A для своих нужд. Получается, что при необходимости использовать класс C в другом проекте, придётся обязательно тащить за ним A и B, или же изменять код класса под нужды данного проекта, что не совсем отвечает концепции повторного использования.

Как же тогда быть, чтобы повторное использование наработанного кода проходило с наименьшими затратами, чтобы не нужно было подстраивать код под текущий проект и чтобы, при необходимости использовать один класс, не пришлось тащить за собой кучу других?

P.S.
Единственное что пришло в голову, это делать все классы абсолютно изолированными друг от друга, кроме конечно самых нужных, типа коннекта к БД и т. п., а для связи двух классов вместе использовать третий объединяющйи класс, но это как-то не очень кажется...

спустя 3 часа 18 минут [обр] Дмитрий Попов(6/509)[досье]

hdd[досье]
Стандартная ситуация - те самые абстрактные классы-драйверы (то что в PS). Но, понятно что их использование слишком усложняет разработку.

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

Вариантов может быть уйма. Как пример - инкапсуляция объекта, передача объекта как свойства.

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

З.Ы. Пример с шаблонизатором не самый правильный, но он наиболее нагляден.

спустя 50 минут [обр] hdd[досье]
Дмитрий Попов[досье]
Почитал, и насколько понял, большей популярностью пользуется передача объекта как свойства, нежели чем классы-драйверы. Про "инкапсуляцию объекта" не совсем понял. Разве это не есть то самое тесное связывание в классе кода и его данных? Тогда как это поможет в моём случае, если одному классу в любом случае нужно обращаться не к данным, а к методам другого объекта. Сами-то по себе эти классы могут быть отлично инкапсулированы.
спустя 14 часов [обр] гоша(0/39)[досье]

hdd
мне кажется, вы немного путаете классы и объекты.

Объекты естественно взаимодействуют между собой, и чем больше, тем лучше.
Классы же действительно должны быть максимально независимы друг от друга. Надо всегда стараться избегать жесткого кодирования имен классов.

Примеры (на пхп)

# очень плохой код

class ActiveRecord {
    function __construct() {
        $this->db = new MyDbClass();

# тоже плохо

class ActiveRecord {
    function __construct() {
        $this->db = MyDbFactory::newInstance();

# использование Локатора гораздо лучше

Locator::register('db', new MyDb);
$r = new ActiveRecord();

class ActiveRecord {
    function __construct() {
        $this->db = Locator::getObject('db');

# а еще лучше Локатор-параметр 

$l = new Locator
$l->register('db', new MyDb);
$r = new ActiveRecord($l);

class ActiveRecord {
    function __construct($locator) {
        $this->db = $locator->getDb();

Как видно, в последнем примере мы добились, что классы ActiveRecord и MyDB независимы и ничего не знают друг о друге. Если к примеру понадобится заменить MyDB на OtherDB, это делается одной сточкой кода.

Локатор это один способ, второй (имхо лучший) Внедрение Зависимости (Dependency Injection). Не знаю, есть ли материал по-русски об этом, на англ. классическая статья Фаулера здесь http://www.martinfowler.com/articles/injection.html

спустя 19 часов [обр] hdd[досье]
гоша[досье]
Спасибо, сейчас стало яснее.
спустя 15 дней [обр] Бубен Пупен[досье]

А я у себя сделал так:

class Сms_Core
{
   public $Template, $Content, $Error;
   
   public function setClass($class, &$object)
   {
      $this->$class = $object;

А потом в скрипте делаю так:

   $Cms->setClass('Template', new Cms_Template);
   $Cms->setClass('Content', new Cms_Content);
   $Cms->setClass('Error', new Cms_Error);

Разве такой способ хуже локатора ?

спустя 1 день 5 часов [обр] Александр aka Efreeti(0/111)[досье]

Бубен Пупен[досье]
Это другой способ.
Однако реализован он у вас кривовато.

  1. Поля незачем делать публичными
  2. Вы повышаете связанность, из-за того, что выносите названия полей класса во вне. Такой способ обычно предпологает следующую схему:
$Cms->setTemplateObject(new Cms_Template);
$Cms->setContentObject(new Cms_Content);
$Cms->setErrorObject(new Cms_Error);

И вам и автору советую почитать про инверсию зависимостей при проектировании ОО систем

Powered by POEM™ Engine Copyright © 2002-2005