Как в php отделить объявление класса от его реализации?

Заранее извиняюсь, если вопрос глупый, Гуглил долго, но так и не нашел ответа.


Допустим, есть класс DataKeeper:

class DataKeeper {
  private $data;

  public function setData($data) {
    // реализация
  }
  public function getData() {
    // реализация
  }
  public function saveData() {
    // реализация
  }
  private function processData() {
    // реализация
  }
}


Тому, кто будет использовать класс нет необходимости знать, как именно работают функции. Ему достаточно знать только о наличии открытых функций, с которыми он может работать.


Раскрытие деталей реализации класса я хочу вынести в отдельный файл, а в «основном» иметь что-то типа:

class DataKeeper {
  public function getData()
  public function setData($data)
  public function saveData()
}



Как это сделать?

Upd:


Спасибо за ответы про интерфейсы. Наверное, я туплю, но мне кажется, это не решение проблемы.


Живой пример:


Вот, допустим, есть у меня файл DataKeeper.php. В нем объявлен интерфейс DataKeeper.

Его реализует класс FileDataKeeper (сохраняет данные в файл), находящийся в своём файле.

class FileDataKeeper implements DataKeeper


Получается, что в проекте, мне все равно надо будет писать:

$dk = new FileDataKeeper;


В файле DataKeeper.php (а только его, по идее, мы должны смотреть) нет ни слова о FileDataKeeper.


Кроме того, если я захочу поменять реализацию хранения, заменив класс FileDataKeeper, например, на MySQLDataKeeper, интерфейс и открытые методы-то не изменятся, а вот «new FileDataKeeper» надо будет менять по всему проекту.
  • Вопрос задан
  • 4314 просмотров
Решения вопроса 1
Skpd
@Skpd
Интерфейсы.

class DataKeeper implements IDataKeeper {
  private $data;

  public function setData($data) {
    // реализация
  }
  public function getData() {
    // реализация
  }
  public function saveData() {
    // реализация
  }
  private function processData() {
    // реализация
  }
}

interface IDataKeeper {
    public function getData();
    public function setData($data);
    public function saveData();
}
Ответ написан
Пригласить эксперта
Ответы на вопрос 12
evilbloodydemon
@evilbloodydemon
Кроме того, если я захочу поменять реализацию хранения, заменив класс FileDataKeeper, например, на MySQLDataKeeper, интерфейс и открытые методы-то не изменятся, а вот «new FileDataKeeper» надо будет менять по всему проекту.


en.wikipedia.org/wiki/Dependency_injection
Ответ написан
Комментировать
deadkrolik
@deadkrolik
Интерфейсы?
Ответ написан
patashnik
@patashnik
В этом и прелесть интерфейсов, потом в коде FileDataKeeper будет использоваться только при создании объекта, а сам объект передаваться по типу DataKeeper, т.е.

$keeper = new FileDataKeeper();
$someService->setDataKeeper($keeper);
// or 
$keeper = new MysqlDataKeeper();
$someService->setDataKeeper($keeper);

class SomeService
{
    // ...
    public function setDataKeeper(DataKeeper $keeper)
    {
         // ...
    }
    // ...
}

Ответ написан
zhulan0v
@zhulan0v
Можно в коде юзать класс DataKeeper, который внутри себя определяет, например, на основании конфига приложения, какой конкретно драйвер использовать — FileDataKeeper или MySQLDataKeeper.

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

А вообще почитайте про паттерны проектирования.
Ответ написан
@balloon
Мне кажется, что:
1. В итоге Вы придете к абстрактному классу
2. Все таки придеться создать dependency container, если вы не хотите что бы родительский класс знал о наследниках.

abstract class DataKeeper {
  protected $data;

  public function setData($data) {
    $this->data = $data;
  }

  public function getData() {
    return $this->data;
  }

  abstract public function save()
  
  abstract public function process()
}

class FileDataKeeper extend DataKeeper {

  public function save() {
    // implementation
  }

  public function process() {
    // implementation
  }
}

class Container {
  protected static $classes = array();  

  public static function register($alias, $class) {
    self::$classes[$alias] = $class;    
  }

  public static function get($alias) {
    if (!isset(self::$classes[$alias]))
	throw new Exception("...");      

    $className = self::$classes[$alias];
    return new $className();
  }
}


// bootstrap.php
Container:register("dataKeeper", "FileDataKeeper");

// SomeWhere.php
$dataKeeper = Container::get("dataKeeper");
Ответ написан
Комментировать
Anonym
@Anonym
Программирую немного )
Если это нужно, исключительно в информативных целях, лучше воспользуйтесь phpDoc. Ну или, как советуют выше, интерфейсом, но тогда, имя класса и имя интерфейса будут отличаться (как я понял, не совсем то, что вам нужно).
Ответ написан
Комментировать
MpaK999
@MpaK999
Буду!
interface, abstract

а чтобы в вашем случае не указывать название класса, то почитайте про Фабрики.
Ответ написан
Комментировать
colonel
@colonel
Разработчик PHP, Laravel
Какую именно вы проблему решаете?
Для чего нужна такая реализация класса?
Ответ написан
ilyaplot
@ilyaplot
PHP программист
Меняйте структуру Вашего кода. Либо сделайте класс, объединяющий все методы хранения.
$dkm = new DataKeeper("mysql");
$dkf = new DataKeeper("file");
Ответ написан
@shagguboy
>Получается, что в проекте, мне все равно надо будет писать:
>$dk = new FileDataKeeper;

Автолоад?
Ответ написан
Комментировать
// в зависимости от конфига или какого нибудь условия $storageMethod = 'имя класса'
$dk = new  $storageMethod;

или я просто не понял вопрос...
Ответ написан
Комментировать
@kambur
Вам все правильно советуют, но нужно все объединить:
1. Для сокрытия реализации класса нужно использовать интерфейсы.
2. Что касается $dk = new FileDataKeeper; - то решение этой проблемы - Фабрика. Вы создаете еще один клас-фабрику Factory с методом скажем getStorrage(); Для каждого варианта хранилища у вас будет свой клас (FileDataKeeper, MySQLDataKeeper ...). Получим:
$dk = Factory::getStorage();
А вот getStorage() будет анализировать ситуацию и возвращать или new FileDataKeeper() или new MySQLDataKeeper() плюс его реализация тоже будет закрыта.
В итоге пользователю мы даем информацию о интерфейсе (доступные методы) и метод для получения класса хранилища.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы