Многомерная карта с использованием объектов в качестве ключей - PullRequest
0 голосов
/ 04 сентября 2018

У меня есть набор объектов (MainObject), которые однозначно определяются двумя объектами (SubObject1, SubObject2) и строкой (theString). Я могу извлечь MainObject из набора, возвращая существующий объект на основе двух подобъектов и строки, если он существует, в противном случае создаю новый, добавляем его в набор и возвращаем этот объект.

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

class SubObject1{}
class SubObject2{}
class MainObject{
    private $subObject1, $subObject2, $theString;
    public function __construct(SubObject1 $subObject1, SubObject2 $subObject2, string $theString):MainObject {
        $this->subObject1=$subObject1;
        $this->subObject2=$subObject2;
        $this->theString=$theString;
    }
}

class ObjectCollection
{
    private $map=[];
    public function getObject(SubObject1 $subObject1, SubObject2 $subObject2, string $theString):MainObject {
        if(isset($this->map[$subObject1][$subObject2][$theString])) {
            $mainObject=$this->map[$subObject1][$subObject2][$theString];
        }
        else {
            $mainObject=new MainObject($subObject1, $subObject2, $theString);
            $this->map[$subObject1][$subObject2][$theString]=$mainObject;
        }
        return $mainObject;
    }
}

$objectCollection=new ObjectCollection();
$subObject1_1=new SubObject1();
$subObject1_2=new SubObject1();
$subObject2_1=new SubObject2();
$subObject2_1=new SubObject2();

$o=$objectCollection->getObject($subObject1_1, $subObject2_1, 'hello');    //returns a new object
$o=$objectCollection->getObject($subObject1_2, $subObject2_1, 'hello');    //returns a new object
$o=$objectCollection->getObject($subObject1_1, $subObject2_1, 'goodby');   //returns a new object

$o=$objectCollection->getObject($subObject1_1, $subObject2_1, 'hello');    //returns existing object

Как это лучше всего реализовать?

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

public function getObject(SubObject1 $subObject1, SubObject2 $subObject2, string $theString):MainObject {
    if(isset($this->map[$theString])) {
        if($this->map[$theString]->contains($subObject1)) {
            $subObject1Storage=$this->map[$theString][$subObject1];
            if($subObject1Storage->contains($subObject2)) {
                $mainObject=$subObject1Storage[$subObject2];
            }
            else {
                $mainObject=new MainObject($subObject1, $subObject2, $theString);
                $subObject1Storage[$subObject2]=$mainObject;
            }

        }
        else {
            $subObject1Storage = new \SplObjectStorage();
            $this->map[$theString][$subObject1]=$subObject1Storage;
            $mainObject=new MainObject($subObject1, $subObject2, $theString);
            $subObject1Storage[$subObject2]=$mainObject;
        }
    }
    else {
        $this->map[$theString] = new \SplObjectStorage();
        $subObject1Storage = new \SplObjectStorage();
        $this->map[$theString][$subObject1]=$subObject1Storage;
        $mainObject=new MainObject($subObject1, $subObject2, $theString);
        $subObject1Storage[$subObject2]=$mainObject;
    }
    return $mainObject;
}

1 Ответ

0 голосов
/ 04 сентября 2018

Логика, которую я имел в виду, была следующей:

Фабрика (или абстрактная фабрика в случае слишком большого количества объектов) позаботится о создании самого объекта.

Контейнер сопоставит уникальные идентификаторы с объектами, созданными фабрикой. И может извлекать объекты на основе этих идентификаторов.

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

namespace Example;

/**
* Class ObjectFactory
*
* @package Example
*/
class ObjectFactory {

/**
 * This is obviosuly not ideal but it can work
 * with a limited amount of objects. Otherwise use an
 * abstract factory and let each instance take care of a few
 * related objects
 *
 * @param string $objectAlias
 *
 * @throws \Exception
 */
public function make(string $objectAlias) {
  switch($objectAlias) {
    case 'object_unique_id_1':
      try{
        $instance = new $objectAlias;
      }catch (\Exception $exception) {
        // log or whatever and rethrow
        throw new \Exception("Invalid class? maybe, I dunno");
      }
    // return $instance
    // etc
  }
}
}

Вы также можете использовать Reflection здесь, чтобы рекурсивно получить аргументы для объекта и вывести новые экземпляры объекта в текущем объекте на основе аргументов в конструкции, по существу, создайте свой собственный маленький контейнер DI.

Но если вы хотите сохранить здравомыслие, используйте что-то вроде Прыщ .


Контейнер:

<?php

namespace Example;

/**
 * Class Container
 *
 * @package Example
 */
class Container {

  /**
   * @var array
   */
  private $map = [];

  /**
   * @param $objectAlias
   * @param $objectInstance
   *
   * @throws \Exception
   */
  public function set($objectAlias, $objectInstance) {
    // You can use a try catch here, I chose not to
    if(isset($this->map[$objectAlias])) {
      throw new \Exception("Already exists");
    }
    $this->map[$objectAlias] = $objectInstance;
  }

  /**
   * @param $objectAlias
   *
   * @return bool|mixed
   */
  public function get($objectAlias) {
    if(isset($this->map[$objectAlias])) {
      return $this->map[$objectAlias];
    }
    return false;
  }
}

Специальный контейнер, который будет содержать ваши собственные методы

<?php

namespace Example;

/**
 * Class ContainerHashMapThingy
 *
 * @package Example
 */
class ContainerHashMapThingy extends Container {
    // Your methods go here
}

И пример объекта:

<?php

namespace Example;

/**
 * Class ExampleObject1
 *
 * @package Example
 */
class ExampleObject1 {

  /**
   * @return string
   */
  public function alias() {
    // This is just for example sake
    // You can just as well have a config, another class to map them or not map them at all
    return 'example_object_1';
  }
}

И реальный пример

<?php

$factory = new \Example\ObjectFactory();
$container = new \Example\Container();

$objectOne = $factory->make('example_object_1');
$container->set('first_object', $objectOne);

Идея в том, чтобы дать вам чистый лист для контейнера + фабрики.

Если вы расширяете контейнер, вы можете реализовывать свои собственные методы, удалять вещи из массива map и даже переписывать метод set в соответствии со своими потребностями.


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

Я очень надеюсь, что это приведет вас на правильный путь.

...