封装和非暴露内部与 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 容器一起使用。
这样的代码不好吗? :
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 容器一起使用。