封装和非暴露内部与 SRP

incapsulation and non exposing internals vs SRP

这样的代码不好吗? :

public class TierType
{
   private const TYPE_HARD = 'hard';
   private const TYPE_MEDIUM = 'medium';
   private const TYPE_SOFT = 'soft';

   private const SUPPORTED_TYPES = [
       self::TYPE_HARD,
       self::TYPE_MEDIUM,
       self::TYPE_SOFT
   ];

   private function _construct(private string $type) {
   }

   public static function fromString(string $tierType): self
   {
       return match ($tierType) {
           self::TYPE_HARD => new self(self::TYPE_HARD),
           self::TYPE_MEDIUM => new self(self::TYPE_MEDIUM),
           self::TYPE_SOFT => new self(self::TYPE_SOFT),
           default =>  throw new Exception('Unsupported tier type provided'),
       };
   }

   public static function createHard(): self
   {
       return new self(self::self::TYPE_HARD)
   }

   public static function createMedium(): self
   {
       return new self(self::self::TYPE_MEDIUM)
   }

   public static function createSoft(): self
   {
       return new self(self::self::TYPE_SOFT)
   }
}

在这个例子中,我们没有公开对象的内部结构,也没有将对象的创建委托给外部世界,这是我喜欢做的方式,并且已经做了一段时间。但最近我听说这是错误的,它破坏了 SRP,因为对象不负责自行创建它,你需要有一个 public 构造函数并从工厂启动对象。像这样:

public class TierType
{
   public const TYPE_HARD = 'hard';
   public const TYPE_MEDIUM = 'medium';
   public const TYPE_SOFT = 'soft';
   public const TYPE_EXTRA_SOFT = 'extra_soft';

   private const SUPPORTED_TYPES = [
       self::TYPE_HARD,
       self::TYPE_MEDIUM,
       self::TYPE_SOFT
   ];

   public function _construct(private string $type) {
       if (!in_array($this->type, self:::SUPPORTED_TYPES) {
           throw new Exception('Unsupported tier type provided')
       }
   }
}

public class TierFactory {

    public function create(string $type): TierType 
    {
       return match ($type) {
           self::TYPE_HARD => new TierType(TierType::TYPE_HARD),
           self::TYPE_MEDIUM => new TierType(TierType::TYPE_MEDIUM),
           self::TYPE_SOFT => new TierType(TierType::TYPE_SOFT),
           default =>  throw new Exception('Unsupported tier type provided'),
       };
    }

   public static function createHard(): TierType
   {
       return new TierType(TierType::TYPE_HARD)
   }

   public static function createMedium(): TierType
   {
       return new TierType(TierType::TYPE_MEDIUM)
   }

   public static function createSoft(): TierType
   {
       return new TierType(TierType::TYPE_SOFT)
   }
}

我觉得也可以,但是在这种情况下可能不需要创建工厂,因为逻辑简单,我们允许其他开发者省略该工厂并直接调用TierType constuctor或创建其他类型的工厂。希望我们在构造函数中有一个验证,但如果没有呢??当您将对象状态委托给许多 classes 时,很难处理对象状态并添加所有验证。从其他人那里我认为 class 应该告诉它自己如何创建它,并提供创建接口。我同意如果创建逻辑很复杂并且它的某些部分与对象无关,那么工厂是有意义的,例如我们需要从不同来源获取数据,以某种方式组合它然后传递给对象,在这种情况下它不是对象责任。

我的想法正确吗???还是真的破坏了SRP,委托给工厂就可以了???

单一职责应该被理解为系统中逻辑任务的抽象。 class 应该承担执行一项特定任务的单一责任。管理自己创作的 class 不一定违反 SRP。

话虽这么说,但对于您的特定示例来说,还有第三种选择似乎更优雅。为每个不同的类型创建单独的 classes,每个 class 扩展自一个共同的 class:

public class TypeHard extends TierType implements TierTypeInterface {

   public function __construct()
   {
       parent::__construct(parent::TYPE_HARD);
   }

}

这种方式也更易于与自动装配的 DI 容器一起使用。