Многомерная карта с использованием объектов в качестве ключей
У меня есть набор предметов (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 ответ
Логика, которую я имел в виду, была следующей:
Фабрика (или абстрактная фабрика в случае слишком большого количества объектов) позаботится о создании самого объекта.
Контейнер сопоставит уникальные идентификаторы с объектами, созданными фабрикой. И может извлекать объекты на основе этих идентификаторов.
Это легкая часть, пользовательская часть должна быть еще проще, вы можете добавлять свои собственные методы, чтобы делать любую магию, которая вам нужна, с псевдонимами и тому подобным.
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
метод для удовлетворения ваших собственных потребностей.
Хотя это не полный ответ, его очень сложно дать, поскольку, как я уже сказал, ваши потребности могут отличаться.
Я надеюсь, что это поможет вам в правильном направлении.