duanbichou4942 2014-01-19 19:59
浏览 15

将对象克隆保存为受保护属性

I intend to create a clone of an object's parent within the constructor of that parent. In short:

class ParentClass {
    protected $property;

    public function __construct() {
        $this->property = clone $this;
    }
}

class ChildClass extends ParentClass {

}

This works all fine, yet the problem with this code is the protected property getting populated with an instance of the ChildClass, in case the ChildClass is instantiated. I would however like it to be an instance of the ParentClass regardless of the class $this refers to.

I could of course combine debug_backtrace and new self() (in order to avoid endless recursion of constructor invocations) and assign the resulting ParentClass instance to the property, though such a soluation is verbose, as debug backtrace only returns string names of caller classes and methods.

Lastly, I could combine new self() and the provision of an argument to the instantiation of the object indicating if a "new self" should be created, but I dislike the solution because of its ugliness and redundancy.

Is there a way in PHP to find a "clone of self"?

  • 写回答

1条回答 默认 最新

  • dqnhfbc3738 2014-01-19 23:15
    关注

    As discussed in the comments, I think the reason this pattern is not working for you is that you have a poorly designed object hierarchy. In the example, ChildClass is a "type of" ParentClass, but also internally references a copy of ParentClass to do some delegated work.

    From the comments, what you have must look something like this:

    class BasicLogger {
        protected $delegated_logger;
    
        public function __construct() {
             // initialise $this->delegated_logger somehow
        }
    
        public function logMessage($message, $flags) {
        {
             $prepared_message = $this->prepareMessage($message, $flags);
             $this->deliverMessage($prepared_message);
        }
    
        private function prepareMessage($message, $flags) {
             // implementation here
        }
    
        protected function deliverMessage($prepared_message) {
             // implementation here
        }
    }
    
    class MailLogger extends BasicLogger {
        protected function deliverMessage($prepared_message) {
             // different implementation here
             if ( $mail_sending_failed ) {
                 $this->delegated_logger->logMessage('Oops, MailLogger failed...');
             }
        }
    }
    

    However, BasicLogger is actually performing multiple roles in the object hierarchy:

    1. defining the interface that all loggers should adhere to (here represented as a single logMessage method)
    2. providing a shared implementation of prepareMessage that all loggers will use, and an implementation of logMessage that depends on it plus a deliverMessage function
    3. providing a specific implementation of deliverMessage that will be completely over-written by child classes
    4. providing a mechanism for complex implementations to delegate to simpler implementations, without a way of distinguishing between the two

    The first three roles should be separated into an interface, an abstract base class, and a simple implementation:

    interface Logger {
        public function logMessage($message, $flags = null);
    }
    
    abstract class BaseLogger implements Logger {
        public function logMessage($message, $flags = null) {
        {
             $prepared_message = $this->prepareMessage($message, $flags);
             $this->deliverMessage($prepared_message);
        }
    
        private function prepareMessage($message, $flags) {
             // implementation here
        }
    
        abstract protected function deliverMessage($prepared_message);
    }
    
    class BasicTextLogger extends BaseLogger {
        protected function deliverMessage($prepared_message) {
             // implementation here
        }
    }
    

    You can then use instances of BasicTextLogger wherever you need, including in other implementations of BaseLogger.

    You might want to put the logic of having a delegated logger (the 4th role of my BasicLogger above) into another class for reuse. BasicTextLogger shouldn't inherit this behaviour, or you'll end up needing to provide a logger to a logger to a logger to a logger, ad infinitum.

     abstract class ComplexLogger extends BaseLogger {
         protected $delegated_logger;
    
         public function __construct( Logger $delegated_logger ) {
             if ( $delegated_logger instanceOf ComplexLogger ) {
                  throw new Exception('Attempted to delegate one complex logger to another; danger of recursion, so disallowed.');
             } else {
                  $this->delegated_logger = $delegated_logger;
             }
         }
     }
    
     class MailLogger extends ComplexLogger {
         protected function deliverMessage($prepared_message) {
             // different implementation here
             if ( $mail_sending_failed ) {
                 $this->delegated_logger->logMessage('Oops, MailLogger failed...');
             }
         }
     }
    

    This then allows you to perform Dependency Injection to provide your complex logger with a simple logger to delegate to:

     $my_logger = new MailLogger( new BasicTextLogger() );
     $my_logger->logMessage('Hello World!');
    

    This may seem like a lot of different classes and interfaces, but each now has a clear responsibility. You could put the whole $delegated_logger logic into MailLogger, but you'd have to copy and paste it if you had another complex logger later. You might also be able to ignore the Logger interface, and just type-hint for classes deriving from the BaseLogger class, but it's possible you'll want an implementation that doesn't use prepareMessage at all - for instance, a DoNothingLogger.

    评论

报告相同问题?

悬赏问题

  • ¥15 装 pytorch 的时候出了好多问题,遇到这种情况怎么处理?
  • ¥20 IOS游览器某宝手机网页版自动立即购买JavaScript脚本
  • ¥15 手机接入宽带网线,如何释放宽带全部速度
  • ¥30 关于#r语言#的问题:如何对R语言中mfgarch包中构建的garch-midas模型进行样本内长期波动率预测和样本外长期波动率预测
  • ¥15 ETLCloud 处理json多层级问题
  • ¥15 matlab中使用gurobi时报错
  • ¥15 这个主板怎么能扩出一两个sata口
  • ¥15 不是,这到底错哪儿了😭
  • ¥15 2020长安杯与连接网探
  • ¥15 关于#matlab#的问题:在模糊控制器中选出线路信息,在simulink中根据线路信息生成速度时间目标曲线(初速度为20m/s,15秒后减为0的速度时间图像)我想问线路信息是什么