【问题标题】:Is it a bad practice to create multiple similar public methods that wrap a private method?创建多个包装私有方法的类似公共方法是一种不好的做法吗?
【发布时间】:2023-03-04 06:27:01
【问题描述】:

我有这个类,它通过接收某些域对象来执行具有副作用的子例程。

这些领域对象是不同的类,但它们有一些相似之处。我想知道如何通过创建许多公共方法来构建服务,这些公共方法用参数包装私有方法,或者只创建一个具有更多参数的公共方法。

案例一:

class MyService {
     private MethodWithManyParameters($a, $b, $c, $d, $similarParameterA, $similarParameterB)   {
          // do things
          if ($similarParameterA == $objectTypeA) {
               // code
          }
          else if ($similarParameterB == $objectTypeB) {
               // other really similar code
          }

     }


     public DoThingsWithObjectA($a, $b, $c, $d) {
          // $this->MethodWithManyParameters($a, $b, $c, $d, $objectTypeA, $variableA);
     }

     public DoThingsWithObjectB($a, $b, $c, $d) {
          // $this->MethodWithManyParameters($a, $b, $c, $d, $objectTypeB, $variableB);
     }
}

// application code
MyService->DoThingsWithObjectA(...);
MyService->DoThingsWithObjectB(...);

案例二

class MyService {
     public DoThingsWithObject($a, $b, $c, $d, $similarParameterA, $similarParameterB)   {
          // do things
          if ($similarParameterA == $objectTypeA) {
               // code
          }
          else if ($similarParameterB == $objectTypeB) {
               // other really similar code
          }
     }
 }

 // application code
 MyService->DoThingsWithObject(........); // this one has a bigger argument list

我正在尝试考虑这两种方法的优缺点。

第一种方法:

优点: - 简化的应用程序代码,在使用大量参数的情况下可以删除一些

缺点: - 引入更多耦合,因为如果引入了 objectC,那么 MyService 也必须更改以适应它

第二种方法:

优点: - 使用较少方法的简化类 - 更灵活

缺点: - 当你必须处理对象之间的差异时,参数列表可以更大

目前看来,只有一个公共函数是好的,但第二种方法一开始看起来更干净,因为它处理的事情更抽象。

我有什么遗漏的吗?

【问题讨论】:

  • 如果你对各种对象做类似的事情,这些对象很可能共享某些属性/方法:创建一个抽象基类,或者实现一个接口,它允许你只使用一种方法,然后让这些类以不同的方式实现各种方法以处理它们的差异。但是,如果两个对象完全不同,则应该实现两个完全独立的方法
  • 所以在您的第一点中,如果 ObjectA 和 ObjectB 非常相似,那么我只需创建两个类:MyServiceA 和 MyServiceB,它们都具有继承自 AbstractMyService 的 DoThingsWithObject?如果对象足够不同,那么当前的第一种方法使用两个单独的方法就足够了?

标签: php class oop object architecture


【解决方案1】:

没有。不是。

查一个很常用的案例:

class ConsoleClass {

     protected /* void */ InternalWrite(/* string */ $SomeValue)   {
          // do things
     } // void InternalWrite(...)

     public /* void */ WriteStr(/* string */ $SomeValue) {
          InternalWrite($SomeValue);
     }

     public /* void */ WriteInt(/* int */ $SomeValue) {
          $SomeString = (string)$SomeValue;
          InternalWrite($SomeString);
     }

     public /* void */ WriteArray(/* array */ $SomeValue) {
          $SomeString = "";

          $SomeString .= "[";
          foreach ($SomeValue as $index => $value)
          {
            $SomeString .= (string)$value;
            $SomeString .= ",";
          } // foreach
          $SomeString .= "[";

          InternalWrite($SomeString);
     }

} // class

// application code
$Console = new ConsoleClass()

$Console->WriteStr("Hello World");
$Console->WriteInt(5);
$Console->WriteArray(array(1, 2, 3, 4, 5));

只要我的 2 美分。

[更新:] 将“私人”更改为“受保护”。

【讨论】:

  • 谢谢,这是一个非常清楚的例子。但是,如果您有一个具有特定控制台标准输出表示的新类型怎么办。像pastebin.com/V9RHvGYe 这样的事情怎么样,只要它们有表示,你就可以编写任意对象。这样,您的控制台类就更加灵活,因为它可以采用具有特定实现的任何对象。然而,这也混淆了对象中的视图逻辑,因为现在它必须知道如何表示自己。想法?
  • @Viktor Chynarov 是的,我同意。在浏览示例之前,我将向对象添加一个类似的示例。请记住您关于通过其他公共方法使用私有/受保护函数的问题;-)
【解决方案2】:

突出的主要问题是在这两种情况下都使用if, elseif。在面向对象的设计中,这几乎总是抽象出代码的情况:

abstract class MyService
{
    abstract public function doThingsWithObject($a, $b, $c, $d);
}

class ObjectA extends MyService
{
    public function doThingsWithObject($a, $b, $c, $d)
    {
        print "Implementation for ObjectA";
    }
}

class ObjectB extends MyService
{
    public function doThingsWithObject($a, $b, $c, $d)
    {
        print "Implementation for ObjectB";
    }
}

然后使用代码:

$objectA = new ObjectA();
$objectB = new ObjectB();

$objectA->doThingsWithObject($a, $b, $c, $d); // Implementation for ObjectA
$objectB->doThingsWithObject($a, $b, $c, $d); // Implementation for ObjectB

【讨论】:

  • 是的,我明白这一点,但情况是代码中的 MyService 更复杂,并且必须与其他对象交互,例如 RepositoryC 和 RepositoryD。在这种情况下,我不想将这些东西添加到 ObjectA 和 ObjectB 中,因为它们有自己特定于域的业务逻辑,而 MyService 的目的是基于其他类对它们进行操作。
  • 在这种情况下,将依赖对象注入到构造函数中。你熟悉依赖注入吗?
  • 是的,我有 DIC 容器来实例化我的控制器,但是我将东西注入到哪个构造函数中,MyService 还是 ObjectA?
  • 这将取决于您的业务需求,以及代码的意义所在。通常这只能通过选择一个选项并查看它的进展来实现。我会先将它们注入您的基类,然后看看您是否需要将它们提取到您的子类中
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-21
  • 2014-02-01
  • 2014-06-18
  • 1970-01-01
  • 2011-12-08
相关资源
最近更新 更多