【问题标题】:Multiple dimensional map using objects as keys使用对象作为键的多维地图
【发布时间】:2018-09-04 11:36:21
【问题描述】:

我有一组对象 (MainObject),它们由两个对象 (SubObject1SubObject2) 和一个字符串 (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;
}

【问题讨论】:

  • 所以您正在寻找与各种工厂相结合的容器,还是我误解了这个?
  • @Andrew 是的,这是正确的。我应该在标签中包含factory,然后添加它。
  • 为什么不走久经考验的路线呢?为每个对象分配一个唯一的 id,将其转储到容器中,并在检索时使用“延迟加载”通过工厂生成新对象。长话短说,您可以传入任意数量的唯一标识符并即时创建它们。天啊,你甚至可以用反射做一些魔术来模拟基于创建的对象构造参数的递归对象创建。当然,这是矫枉过正,但它真的很干净。我想你也可以使用一些组件(想到 Symfony 的 DI)。如果你愿意,我可以给你一些写得不好的伪代码。
  • @Andrew 我认为您的想法是正确的。为每个人分配一个唯一的 ID。几乎SplObjectStorage 使用对象的哈希所做的事情。理想情况下,就我而言,它不是单个对象的拥有,而是两个对象加上字符串的拥有。您如何看待创建一个新类,该类的单一方法是一个构造函数,该构造函数保存两个对象加上字符串,并在 SplObjectStorage 中使用该对象?编辑。也许不是,因为它们是不同的实例......
  • 所以你基本上想要一个不可变的哈希映射(或者一个希望在 php 世界中不会被修改的数组)?但是在您的情况下,它应该是 2 个对象的唯一哈希(我假设是连接的)以及您碰巧传入的任何字符串?这个好像有点臭。理想情况下,除非您有充分的理由不这样做,否则您希望将每个对象哈希分开,这允许您将来通过任何您想要的方式组合您想要的任何哈希。至于字符串本身,那就是另一回事了,我想你可以拥有任意数量的。

标签: php collections set factory splobjectstorage


【解决方案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 容器。

但是,如果您想保持理智,请使用 Pimple 之类的内容。


容器:

<?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 方法以满足您自己的需要。


虽然这不是一个完整的答案,但很难给出一个答案,因为正如我所说,您的需求可能会有所不同。

我希望这能让你走上正轨。

【讨论】:

  • 谢谢 Andrew,我不明白你对 alias 的使用。它是使用哈希或其他东西动态确定的吗?
  • 另外,我可能误解了您的评论There's no real clean way to do this with unlimited arguments except maybe ...$args but it's more or less the same thing only more verbose,但如果我理解正确,php.net/manual/en/… 可能会对您有所帮助。
  • 操作,抱歉。我做了一点重构。一定是我的错误留下了那条评论。
  • alias 部分用于将每个对象标识为唯一标识符。在我的ExampleObject1 中,我只是将它用作硬编码值。无论如何都不理想,通常你想要某种yaml 文件或任何你喜欢的文件并将每个类映射到它,然后解析yamls并延迟加载类。但这似乎是严重的矫枉过正。简而言之,您可以定义自己的别名,只要它们是唯一的,您甚至可以继续使用 Spl 对象作为容器并简单地替换我示例中的那个。或者甚至更好地将 Spl 包装在一个类中并进一步抽象它。
猜你喜欢
  • 1970-01-01
  • 2019-05-03
  • 2015-05-13
  • 2022-01-25
  • 1970-01-01
  • 2018-06-28
  • 2010-11-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多