【问题标题】:Difference between singleton and factory pattern单例模式和工厂模式的区别
【发布时间】:2011-01-06 20:14:44
【问题描述】:

我是设计模式的新手,我真的看不出这两种模式之间的区别,它们都是创建模式吗?每种模式的目的是什么?谢谢。

【问题讨论】:

标签: design-patterns


【解决方案1】:

单例模式可确保您始终返回相同您正在检索的任何类型的实例,而工厂模式通常为您提供每种类型的不同实例。

单例的目的是您希望所有调用都通过同一个实例。这方面的一个例子可能是管理磁盘缓存或从静态字典中获取数据的类;只要重要的是只有一个已知实例与资源交互。这确实降低了它的可扩展性。

工厂的目的是创建和返回新的实例。通常,它们实际上根本不是同一类型,但它们将是同一基类的实现。但是,每种类型可能有很多实例

【讨论】:

  • 有点小题大做——单例与对象的生命周期有关(在这种情况下只有一个——可能是应用程序的生命周期)。工厂的工作是返回您需要的对象——它通常会构建一个类型的新实例,但不一定如此——可以将单例的生命周期控制从单例类中移出并将其放入工厂,而不是这样每次都返回相同的类,使其本质上是您的应用程序的单例
【解决方案2】:

单例模式是一种模式,其目的是确保无论客户端(或多个客户端)请求为他们创建(实例化)此特定类型的实例多少次,它们总是会得到完全相同的该类型的一个且只有一个实例。它保证只能实例化该类型的一个实例(例如,如果该类型在内存中管理某个数据结构的 200 MByte 缓存副本 - 您只希望存在一个副本)单例类型通常会有一个工厂方法,向请求的客户提供一个且只有一个实例。

工厂方法是一种封装类型的创建或实例化的方法,以便类型本身可以控制它的实例化方式。

【讨论】:

    【解决方案3】:

    单例模式确保只存在一个类的实例,并且通常提供一个众所周知的,即全局点来访问它。

    工厂模式定义了一个用于创建对象的接口(对数量没有限制),并且通常抽象出对要实例化哪个类的控制。

    【讨论】:

      【解决方案4】:

      单例

      当只需要一个资源的单个实例时,单例模式是许多应用程序中经常使用的模式。 PHP 网页最明显的资源类型是数据库连接,但也可以使用其他资源类型。在获取或动态创建网页时,可能需要进行多个数据库调用。如果可以使用资源的单个实例,而不是创建多个连接,则可以最大限度地减少开销。在这种情况下,单个实例是由单例模式创建的。

      <?php
      class db {
          /*** Declare instance ***/
          private static $instance = NULL;
          
          /**
           * the constructor is set to private so
           * so nobody can create a new instance using new
           */
          private function __construct() {
              /*** maybe set the db name here later ***/
          }
      
          /**
           * Return DB instance or create intitial connection
           *  @return object (PDO)
           * @access public
           */
          public static function getInstance() {
              if (!self::$instance) {
                  self::$instance = new PDO("mysql:host='localhost';dbname='animals'", 'username', 'password');
                  self::$instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
              }
              return self::$instance;
          }
          /**
           * Like the constructor, we make __clone private
           * so nobody can clone the instance
           */
          private function __clone() {
      
          }
      } /*** end of class ***/
      ?>
      

      让我们看看上面的单例类发生了什么。变量名称$instance 被创建并生成private,这样可以确保没有人尝试直接访问它。同样,构造函数和 __clone 方法已设为私有,以防止克隆类或试图实例化它的实例的人。该类有一个提供资源的方法,getInstance() 方法。

      getInstance() 方法检查实例是否不存在。如果不存在实例,则创建 PDO 类的新实例并将其分配给 $instance 变量。如果现有实例可用,getInstance() 方法将返回此值。结果是返回的值始终是同一个实例,不需要新的资源或开销。

      这是如何在应用程序中使用它的一个小演示。

      <?php
      try {
          /*** query the database ***/
          $result = DB::getInstance()->query("SELECT animal_type, animal_name FROM animals");
      
          /*** loop over the results ***/
          foreach ($result as $row) {
              print $row['animal_type'] .' - '. $row['animal_name'] . '<br />';
          }
      } catch (PDOException $e) {
          echo $e->getMessage();
      }
      ?>
      

      工厂

      工厂模式是一个为你创建对象的类,而不是你需要使用 new 关键字来自己创建一个。工厂,顾名思义,就是创建对象的工厂。我们为什么需要这个?让我们考虑一个使用ini 文件作为配置选项的应用程序。然后更改应用程序以从数据库中获取配置选项。随着基础被移除,应用程序的其余部分就像纸牌屋一样分崩离析。 这种对象的“紧密耦合”,每个对象都严重依赖另一个对象,这会在更大的应用程序中产生问题。需要一个对象可以相互引用但不相互依赖的系统。
      在我们的配置文件变成数据库的例子中,如果其他类都依赖于从ini文件中读取的类,而这突然被委托给数据库类就会出现问题。
      通过使用工厂设计模式,如果您将对象的类型从ini 阅读器类更改为数据库类,您只需更改工厂即可。任何其他使用工厂的代码都会自动更新。

      <?php
      /**
       * @config interface
       */
      interface Config {
          function getName();
      }
      
      /**
       * @config class
       */
      class userConfig implements Config {
          /*
           * @username
           */
           public $user_id;
      
           /*** contructor is empty ***/
           public function __construct($id) {
              $this->user_id = $id;
           }
      
           public static function Load($id) {
              return new userConfig($id);
           }
      
           public function getName() {
               try {
                   /*** query the database ***/
                   $sql = "SELECT username FROM user_table WHERE user_id=:user_id";
                   $db = db::getInstance();
                   $stmt = $db->prepare($sql)
                   $stmt->bindParam(':user_id', $this->user_id, PDO::PARAM_INT);
                   return $stmt->fetch(PDO::FETCH_COLUMN);    
              } catch (PDOException $e) {
                  /*** handle exception here ***/
                  return false;
              }
          }
      } /*** end of class ***/
      
      /*** returns instance of config ***/
      $conf = userConfig::Load( 1 );
      echo $conf->getName();
      ?> 
      

      对于简单地检索用户名来说,这似乎有点过多的代码。但是在从基于文件到数据库检索的更改的大规模应用程序中,结果可能是灾难性的。这里只需更改 getName() 方法检索名称,返回的对象是相同的,从而允许应用程序的其他需要用户名的部分,例如:购物车的登录,继续他们以前做过。

      【讨论】:

        【解决方案5】:

        Singleton 总是返回同一个类的实例,工厂可以通过实例化各种类来创建对象,或者使用其他分配方案如对象池。所以可以在工厂模式中使用单例

        【讨论】:

          【解决方案6】:
          Singleton Factory
          Returns Same instance Returns various new instances
          Single Constructor hiding Multiple Constructors expose
          No interface Interface driven
          No Subclasses Subclasses

          【讨论】:

            猜你喜欢
            • 2017-07-31
            • 1970-01-01
            • 2010-10-11
            • 2010-11-09
            • 1970-01-01
            • 2018-08-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多