Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 通过方法调用更改对象的类型,而不返回新实例_Php_Oop_Polymorphism - Fatal编程技术网

Php 通过方法调用更改对象的类型,而不返回新实例

Php 通过方法调用更改对象的类型,而不返回新实例,php,oop,polymorphism,Php,Oop,Polymorphism,最近我偶然发现了一个关于面向对象设计的问题 关于背景: 我决定创建一个很好的旧todo应用程序来了解更多关于 PHPUnit和可测试PHP。要求是一贯的 检索/存储/编辑任务。TODO应该存储在MySQL中 数据库 我的想法是创建一个基类Todo,它保存一个PDO实例 以及todo的id和标题(相当于其 表示(在数据库中) 此外,还应有两种工厂方法负责 创建不同TODO的实例。这些是: 应用程序前端定义的新TODO 已经保存了todos 原因是存储数据的行为不同 数据库中的当前状态(标题/说明)

最近我偶然发现了一个关于面向对象设计的问题

关于背景:
我决定创建一个很好的旧todo应用程序来了解更多关于 PHPUnit和可测试PHP。要求是一贯的 检索/存储/编辑任务。TODO应该存储在MySQL中 数据库

我的想法是创建一个基类
Todo
,它保存一个PDO实例 以及todo的id和标题(相当于其 表示(在数据库中)

此外,还应有两种工厂方法负责 创建不同TODO的实例。这些是:

  • 应用程序前端定义的新TODO
  • 已经保存了todos
  • 原因是存储数据的行为不同 数据库中的当前状态(标题/说明)

    我在没有子类的情况下的第一次尝试如下所示:

    class Todo {
        [...] // fields and other methods
    
        public function save() {
            $statement = '';
            $parameters = [];
    
            if (is_null($this->_id)) {
                $statement = 'insert into todos (title) values(?)';
                $parameters = [$this->_title];
            } else {
                $statement = 'update todos set title = ? where id = ?';
                $parameters = [$this->_title, $this->_id];
            }
    
            $prepared_statement = $this->_pdo->prepare($statement);
            $result = $prepared_statement->execute($parameters);
    
            return $result;
        }
    }
    
    if语句显示我基本上只是检查了 待办事项。换句话说:待办事项是我想要的新待办事项吗 使用insert语句存储,或者todo已在 数据库,因此我必须使用update语句来处理它

    为了摆脱if语句,我引入了子类
    NewTodo
    SavedTodo
    。 最后,我得出以下结论:

    abstract class Todo {
        [...] // fields and other methods
    
        public abstract function save();
    }
    
    class NewTodo extends Todo {
        public function save() {
            $statement = 'insert into todos (title) values(?)';
            $parameters = [$this->getTitle()];
    
            $prepared_statement = $this->getPdo()->prepare($statement);
            $result = $prepared_statement->execute($parameters);
    
            return $result;
        }
    }
    
    class SavedTodo extends Todo {
        public function save() {
            $statement = 'update todos set title = ? where id = ?';
    
            $parameters = [
                $this->getTitle(),
                $this->getId()
            ];
    
            $prepared_statement = $this->getPdo()->prepare($statement);
            $result = $prepared_statement->execute($parameters);
    
            return $result;
        }
    }
    
    虽然这对我来说似乎更合理,但我现在面临的是实际问题 这种方法存在问题

    我——以一种天真的方式——希望有一个调用 之后,
    save()
    方法将成为
    SavedTodo
    的实例

    显然,在我的实现中并非如此

    在“介绍墙”之后,我的问题是:

    有没有办法在PHP中实现这一点而不返回新实例

    也许更重要的是:

    实施这种行为是否合理


    我认为尝试通过对象的类型来表示对象状态不是一个好主意。正如您所指出的,当它被保存时,您会做什么,然后它应该是一个
    SavedTodo
    。即使您实现了它,使用它也将是一个奇怪的结构。例如:

    $t = new NewTodo();
    $t->content = 'testing';
    $savedTodo = $t->save();
    $t = $savedTodo; // need to overwrite the original Todo with the SavedTodo
    
    $todoMapper = new TodoMapper($pdo);
    
    $todo = new Todo();
    $todo->content = 'testing';
    
    $todoMapper->save($todo);
    
    正如您所看到的,如果您从局外人的角度来看代码,这并不是您想要期望必须做的事情

    第一种情况下的if语句不是需要解决的问题。在OOP中,继承是一个特性,而不是一个要求,所以如果它不适合工作,就不要使用它


    OOP中的一个很好的经验法则是loosley遵循,这基本上是为您的类指定明确的角色,而这些角色并没有做太多的工作。理想情况下,他们应该承担单一责任

    就个人而言,我不会将save方法放在Todo上,因为它是一个实体,其职责是在系统中表示Todo。赋予Todo保存自身的能力可能被认为是另一项职责,因此我更希望有一个处理获取、插入、更新和删除的TodoMapper类

    此外,系统中的Todo实体不应该关心系统使用什么样的数据存储方法,或者什么类型的数据库等等,甚至不应该关心是否存储了数据

    例如:

    $t = new NewTodo();
    $t->content = 'testing';
    $savedTodo = $t->save();
    $t = $savedTodo; // need to overwrite the original Todo with the SavedTodo
    
    $todoMapper = new TodoMapper($pdo);
    
    $todo = new Todo();
    $todo->content = 'testing';
    
    $todoMapper->save($todo);
    
    在这个系统中,映射程序可以检查Todo是否有ID,并相应地进行更新或插入。或者,如果愿意,可以在mapper类上使用
    insert()
    update()
    方法。 这种设计的另一个好处是Todo不需要PDO对象,因为它本身不涉及CRUD操作


    Martin Fowler提供了有关的更多信息。

    感谢您的精彩解释。您的第一个示例正好显示了我试图避免的内容。关于一个对象在不实际分配新类型的情况下更改其类型的问题在编程时刚刚出现。