【问题标题】:Singleton to factory in PHP单例到 PHP 工厂
【发布时间】:2016-05-30 06:42:39
【问题描述】:

关于这方面的话题已经很多了,但我并不清楚为什么工厂模式比单例模式更好。

一个例子

class MySingleton {
 public static function numberByWhat( $number ) {
   return $number + 100;
 }
 public static function someCharacters( $string ) {
   return $string + 'abc';
 }
}

class MyOtherSingleton {
  public static function getImage( $url ) {
    return '<img src="' . $url . MySingleton::numberByWhat( $50 ) . '">';
  }
  public static function getTextById( $id ) {
    if( $id == 3 ) {
      return 'this is my text' . MySingleton::someCharacters( 'Yeah' );
    }
  }
}

function run() {
  echo MyOtherSingleton::getImage( 'http://www.example.com/image.png' );
  echo MyOtherSingleton::getTextById( 3 );
}

run();

所以我们有 2 个类和一个函数。当我run() 时,我在第二堂课中启动了一个函数。在第二个类的函数中调用了对第一类的调用。

  • 这看起来像工厂模式吗?
  • 为什么作为工厂模式更好?

【问题讨论】:

  • 这看起来既不像工厂模式也不像单例模式。现在,您只需将一些函数分组在某些类中。测试类实例的状态变得很困难。
  • Factory 创建了一些对象。您的代码中哪里有对象?
  • 你可以在这个例子中完全摆脱类并使用简单的原始全局函数。类在这里添加的唯一东西是一小部分命名空间。你不是真正的面向对象领域,因此这个问题没有什么意义。
  • 虽然您的问题有点偏离,但我在想,从一些不正确的假设来看,我试图在我的回答中解释这两种模式以及另一种相关模式。希望对您有所帮助!

标签: php class singleton factory


【解决方案1】:

所以你给出的例子既不是单例也不是工厂。您在这里拥有的只是“静态类”——只有静态方法和属性的类。工厂和单例也不能真正解决同一个问题,因此很难对它们进行比较和对比。

单身人士

Singleton 用于管理共享状态或避免在您真正需要“某物之一”时多次实例化一个类的开销。

这是一个单例的例子:

class DatabaseConnection {
    // Static reference to the single instance of this class we maintain.
    protected static $instance;

    // Normal instance properties.
    protected $mysql;

    // Protected constructor to prevent "new DatabaseConnection();"
    protected function __construct() {
        $this->mysql = new MySQLConnection("localhost", 3306, "appdb");
    }

    public static function getInstance() {
        if (!self::$instance) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    public function query($sql) {
        return $this->mysql->query($sql);
    }
}

当你想在你的代码中使用DatabaseConnection()时,你可以这样做:

function getUser($userId) {
    $userData = DatabaseConnection::getInstance()->query("SELECT * FROM ...");
}

DatabaseConnection() 仅在第一次使用时实例化一次。永远不会创建超过一个。

依赖注入/控制反转

使用单例的竞争策略基本上是Dependency Injection 或Inversion of Control。使用依赖注入,不是拥有一个共享的、静态存储的类实例,而是简单地将实例从类传递到类和函数到函数。这是一个简单的例子:

class DatabaseConnection {
    // Normal instance properties.
    protected $mysql;

    public function __construct() {
        $this->mysql = new MySQLConnection("localhost", 3306, "appdb");
    }

    public function query($sql) {
        return $this->mysql->query($sql);
    }
}

class UserManager {
    protected $databaseConnection;

    public function __construct(DatabaseConnection $databaseConnection) {
        $this->databaseConnection = $databaseConnection;
    }

    public function lookupUser($userId) {
        return $this->databaseConnection->query("SELECT ...");
    }
}

依赖注入的一个优点是您可以更轻松地测试代码。例如,您可以扩展 DatabaseConnection 并将其命名为 TestDatabaseConnection,并使其实际上不使用真正的 MySQL 数据库,这可以使您的测试更快、更可靠。所以,争论的焦点是单例和依赖注入/控制反转,而不是工厂。

工厂

所以,现在,Factories:工厂简化了对象的创建,返回不同类/子类的对象,并帮助从“模板”创建对象。例如,假设我们有不同的类来代表不同类型的用户:

class User {
    // ...
}

class ModeratorUser extends User {
    // ...
}

class AdminUser extends ModeratorUser {
    // ...
}

class GuestUser extends User {
    // ...
}

这些类中的每一个都包含对使用特定类型的用户帐户很有用的方法和属性。那么,您将如何通过用户 ID 创建和返回这些用户对象之一,以确保返回正确的用户类型?我们可以使用工厂模式:

class UserFactory {
    public static function fromUserId(DatabaseConnection $db, $userId) {
        $record = $db->query("SELECT * FROM users WHERE user_id ...");
        // ...

        switch ($record['type']) {
            case 'admin':
                return new AdminUser($userId);
                break;

            case 'moderator':
                return new ModeratorUser($userId);
                break;

            case 'guest':
                return new GuestUser($userId);
                break;

            default:
            case 'normal':
                return new User($userId);
                break;
        }
    }
}

然后,要加载正确的 User 类,您只需调用:

$user = UserFactory::fromUserId($userId);

如果是管理员帐户,则为AdminUser;如果是访客帐户,则为GuestUser。

【讨论】:

  • 所以在最后一个...静态函数调用一个扩展类,它使用一种基类。这可能是迄今为止我见过的最简单的例子。从现在开始,我可能会将静态函数与扩展类结合起来。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-25
相关资源
最近更新 更多