我知道有很多关于不同工厂模式的差异的问题,但答案是如此不同和令人困惑。我读过的书籍使用不清楚和(简化)简化的例子。即使在阅读维基百科的解释之后,我也有很多问题,以及关于它们的大量在线解释,包括所有这些网站上的解释。我正在阅读的这本书是Head First Design Patterns。
在Simple Factory中,客户端使用单独的类(Creator)和工厂方法(可以是静态的)来返回产品。
在工厂方法模式中,创建者和客户端是相同的东西,他们在同一个类中使用抽象方法来创建新的产品,它们在同一个类中运行。当然,造物主(或客户)是抽象的,因此关于制作混凝土产品的决定被推迟到子类。
我的理解是否正确(例如,FMP中的客户端和创建者是相同的,我从未在FMP图中看到客户端)?
在工厂方法模式中,它表示创建方法不能在创建者之外重复使用,因此只能在创建新的创建者时重复使用它?
在哪种情况下我可以选择其中一种?
(P.S。请不要将此标记为重复,我希望在此网站上明确这一点)
答案 0 :(得分:5)
Simple Factory是类形式的工厂。因此,它不能解决优雅问题,因为对于Product的每个新子类,您都必须在create()方法中编辑switch语句。这违反了打开/关闭原则。使简单工厂变得有用的一种可能方法是在这里使用类注册:http://www.oodesign.com/factory-pattern.html
工厂方法是方法形式的工厂(因此名称)。这并不违反开放/关闭原则,因为您通过扩展处理更改而不是修改代码。
您的理解是正确的。 FMP中的客户端和创建者/工厂是相同的,因为工厂(方法)是客户端的一部分。
FMP中的create方法确实不可重用。这是可以的,因为这不是尝试创建应用程序范围的产品工厂,而是客户端创建其依赖对象而不使用 new 的方法。
我不能回答你的第三个问题,因为我认为这是基于偏好。
答案 1 :(得分:3)
简单工厂 - 这不是官方的GoF模式,事实上我不知道你在谈论什么,直到我拿出我的 Head First Design Pattern Book。 Simple Factory只是一种可以返回不同硬编码子类型的方法。
public Pizza createPizza(String type){
if(type.equals("cheese")){
return new CheesePizza();
}else if (type.equals("pepperoni")){
return new PepperoniPizza();
}
...
}
此代码存在的问题是您只能使用硬编码类型。如果要更改方法的工作方式或返回的类型,则必须修改代码并重新编译。添加新类型将非常困难。
工厂方法 - 您在超类中完成大部分工作,但决定在运行时使用哪种对象。通常,超类需要创建某种默认类型的工作对象,但超类允许子类专门化工作者。工厂方法通常在AbstractFactory过度使用时使用,但一个缺点是它会强制您使用具有自己的一组维护和设计问题的继承。这与SimpleFactory非常相似,除了使用继承而不是鉴别器来获取不同的返回类型。
public void foo(){
Bar bar = createBar();
//do stuff with bar
}
//method is protected so subclass can override it to return something else
protected Bar createBar(){
return new DefaultBar();
}
AbstractFactory - 创建对象只知道它们实现的接口而不是实际的类。 AbstractFactories使代码可以轻松地在不同的系统中工作,因为您不需要知道所使用的具体工厂或具体产品。
例如Collection.iterator()
是Iterator
个对象的抽象工厂。 LinkedList
或HashSet
等类具有自己的iterator()
实现(因此是具体工厂),它们返回实现迭代器接口的不同类(具体产品)
一旦你完成 Head First Design Patterns 我推荐模式上的Holub 代码有点陈旧(前仿制药)但你真的学到了很多关于多种模式的方法在非平凡的代码示例中彼此交互。这本书只有2个代码样本,每个代码样本覆盖大约10个模式,每个样本分别用100多页来解释
答案 2 :(得分:2)
您正在看到两种工厂方法样式的示例,因为有两种完全不同的情况,它们是合适的。
第一种类型 - 它似乎与你被称为简单工厂的内容相吻合,当你有一个相对复杂的对象时,由于它的复杂性而难以创建。 / p>
这里的经典示例是Pizza
类,它具有PizzaConstructor
类(或类似的名称),其中构建Pizza
对象所需的大部分聪明人都被编码到{ {1}}。
有一个很好的讨论here,但重点是这个形式在Constructor
中如何构建 Pizza
而不是阻碍在Factory
班级。
使用这种技术,您可以使构造函数代码比通常更清晰。
Pizza
使用工厂的第二种情况是,您需要您的类能够实际制作提供类型的对象。如果没有用户提供工厂机制供您使用,这很难。然后,用户提供某种工厂机制,可能是工厂对象,也可以扩展抽象类并提供具体的构造方法。
答案 3 :(得分:1)
简单工厂:
定义:
创建对象而不将实例化逻辑暴露给客户端。 通过通用接口引用新创建的对象
public interface PaymentMethod {
public void makePayment();
}
class CreditCard implements PaymentMethod {
public void makePayment() {
System.out.println("Payment through credit card...");
}
}
class NetBanking implements PaymentMethod {
public void makePayment() {
System.out.println("Payment through net banking...");
}
}
public class PaymentMethodFactory {
public static PaymentMethod getPaymentMethod(String method) {
if ("creditcard".equalsIgnoreCase(method)) {
return new CreditCard();
} else if ("netbanking".equalsIgnoreCase(method)) {
return new NetBanking();
} else {
throw new IllegalArgumentException("Payment method not supported!");
}
}
}
public class SimpleFactoryTest {
public static void main(String[] args) {
PaymentMethodFactory factory = new PaymentMethodFactory();
PaymentMethod paymentMethod = factory.getPaymentMethod("creditcard");
paymentMethod.makePayment();
}
}
工厂方法:
定义:
定义用于创建对象的接口,但让子类决定实例化哪个类 通过公共接口引用新创建的对象。
public interface PaymentMethod {
public void makePayment();
}
class CreditCard implements PaymentMethod {
public void makePayment() {
System.out.println("Payment through credit card...");
}
}
class NetBanking implements PaymentMethod {
public void makePayment() {
System.out.println("Payment through net banking...");
}
}
public interface IPaymentMethodFactory {
public PaymentMethod getPaymentMethod();
}
class CreditCardFactory implements IPaymentMethodFactory {
public PaymentMethod getPaymentMethod() {
return new CreditCard();
}
}
class NetBankingFactory implements IPaymentMethodFactory {
public PaymentMethod getPaymentMethod() {
return new NetBanking();
}
}
public class FactoryMethodTest {
public static void main(String[] args) {
IPaymentMethodFactory factory = new CreditCardFactory();
PaymentMethod paymentMethod = factory.getPaymentMethod();
paymentMethod.makePayment();
}
}
答案 4 :(得分:0)
首先,我没有看到"工厂方法模式"作为一个独立的模式(从未对这个模式负责,直到我按照你在你的问题中所说的那样阅读维基百科)。我把它看作是工厂模式+战略模式的混合体。
您可以将对象工厂想象为一个简单的对象创建者 当策略模式在对象工厂内部发挥作用时,您开始添加关于对象创建的更多逻辑,以将其从客户端隐藏(客户端不应该知道如何创建对象,将此责任留给工厂)。
工厂可以是多种类型(工厂创建的决定取决于许多因素):
- 在考虑对象关联时,对象可以由多个对象组成。这里根对象可以有一个工厂方法来创建需求对象。此工厂方法负责检查新添加的对象是否使根保持有效状态。所以这里的战略模式(你称之为工厂方法模式)可能会发挥作用:
class Car {
public $carType;
public $audioSystem;
public function __construct($carType) {
$this->carType = $carType;
$this->audioSystemFactory();
}
public function audioSystemFactory() {
if ($this->carType == 'hipster') {
$this->audioSystem = new VynilPlayer();
}
else {
$this->audioSystem = new Mp3Player();
}
}
public function startMusic($albumName) {
$this->audioSystem->playSongs($albumName);
}
}
class VynilPlayer {
public $vynilAlbums = array('MJ TOP HITS', 'JUSTIN BIEBER TOP HITS');
public function playSongs($albumName) {
$this->loadVynil();
$this->startPlayer();
}
public function loadVynil() {
}
public function startPlayer() {
}
}
class Mp3Player {
public $dvdAlbums = array('MJ TOP HITS', 'JUSTIN BIEBER TOP HITS');
public function playSongs($albumName) {
$this->loadDVD();
$this->startPlayer();
}
public function loadDVD() {
}
public function startPlayer() {
}
}
您还可以拥有一个工厂类,该工厂类只负责创建一种类型的对象。当创建逻辑非常复杂时,您为每个对象类型创建一个工厂类。
即使像类构造函数这样的原始形式的实例也可以被视为对象工厂。
使工厂隐藏某些对象的实现逻辑。您可以将策略模式添加到工厂以隐藏客户端的这些详细信息。
答案 5 :(得分:0)
这是一个我能想到的简单指南 -
这两者之间的基本区别是一个提供了一个具体的创建者(简单工厂)而另一个(工厂方法)支持通过继承解耦创建者,以便可以支持多个创建者。一个让它更清晰的例子 -
Button
- Toggle
,ImageButton
等。您更有可能拥有不同的Button创建者。例如。基于不同的操作系统,WindowsButtonFactory, OSXButtonFactory
等。在这种情况下,工厂方法适用于由ButtonFactory
实现的抽象类WindowsButtonFactory, OSXButtonFactory
(以及将来还有更多)。Book
- Fiction, NonFiction
等。通常你会认为具体的工厂应该足够了。