8

我是OOP和学习设计模式的新手,所以我写了一些简单的代码来尝试一个Factory方法,除了当我想添加另一个子类型时,一切似乎都很好。这里是到目前为止的代码:在java中扩展参数化工厂方法

public interface Person { 
    public String getDescription(); 
} 

public class Adult implements Person { 
    @Override 
    public String getDescription() { 
    return "I am an ADULT"; 
    } 
} 

public class Child implements Person { 
    @Override 
    public String getDescription() { 
    return "I am a CHILD"; 
    } 
} 

public class PersonFactory { 
    public Person create(int age) { 
    if (age < 18) return new Child(); 
    return new Adult(); 
    } 
} 

public class ClientA { 
    public static void main(String[] args) { 
    PersonFactory personFactory = new PersonFactory(); 
    Person person; 
    person = personFactory.create(80); 
    System.out.println(person.getDescription()); 
    } 
} 

如果要求后发生变化,包括一个子类Pensioner因为当年龄> 70,我将不得不采取:

添加行if (age > 70) return new Pensioner();create()方法在PersonFactory类中,这肯定会打破开闭原则?或者,正如“四人帮”设计模式书中所建议的,重写参数化工厂方法来选择性地扩展Creator生成的产品。在这种情况下,我认为,这将意味着写一个新的类:

public class PersonFactoryWithPensioner extends PersonFactory { 
    @Override 
    public Person create(int age) { 
    if (age > 70) return new Pensioner(); 
    return super.create(age); 
    } 
} 

现在,这意味着要改变或者所有这些调用PersonFactory现在将有客户端使用PersonFactoryWithPensioner,而不是,或我必须承认,新客户可以拨打PersonFactoryWithPensioner,而老客户如。当年龄大于70岁时,ClientA仍然只会收到Adult的对象。如果后来出现在另一个子类中,它会变得更糟。 Infant已被添加。为确保新客户接收Infant,Child,AdultPensioner的任何对象是合适的,新类别PersonFactoryWithInfant将不得不延长PersonFactoryWithPensioner。这不可能是正确的,似乎更可能是我误解了GoF的建议。

我的问题是:有没有办法添加一个新的子类型,可以返回到旧客户端而不改变它们,并且不需要通过更改PersonFactory代码来包含新子类型来打破OCP?

道歉,如果我没有正确发布,这是我第一次在这里发布一个问题。我已经通过以前的答案看过类似的问题,但他们似乎没有解决这个问题。

+0

您是否阅读过这篇文章:http://softwareengineering.stackexchange.com/questions/302780/does-the-factory-pattern-violate-the-open-closed-principle – jaudo

+0

这不是工厂方法模式。这个例子通常被称为简单工厂。这不是一个GoF模式。 – jaco0646

回答

1

问题是“客户是否期望在没有代码修改的情况下创建养老金领取者对象?”。如果是,那么你应该打破“关闭”规则并更新你的工厂代码。如果没有,那么你应该创建一个新工厂,客户将使用它。

2

规则有时意味着被打破,所以我说BREAK封闭原则,以保持干净和简单。在我看来,为每种类型的人创建多个工厂类的开销会打破整个工厂方法的目的。打破封闭原则允许您有一个单一类创建任何类型的人。

public Person create(int age) { 
    if (age < 4) return new Infant(); 
    if (age < 18) return new Child(); 
    if (age < 70) return new Adult(); 
    return new Pensioner(); 
    } 
+0

正如所提到的,这种修改会打破“关闭”的原则 – jaudo

+0

好吧,我将不得不考虑什么封闭的原则,谢谢@ julaudo –

+0

虽然这确实打破OC,我已upvoted,因为我觉得它是完全有道理的这样做 - 我自己的答案,我认为是过度设计的大多数要求,同样与@alayor的 – tucuxi

2

我认为OCP不会停止修改任何方法或类。

但是,它建议如果你需要做任何修改,你应该这样做,以便你不必再次修改该代码。

鉴于您稍后可能需要修改PersonFactory - 您可以创建另一个Factory类来创建PersonFactory类型的对象。虽然这看起来像过度设计的解决方案。

另一个可能的解决方案是PersonFactory从某些动态源加载此规则,例如,将此规则保存在使用JSON格式的文件中。 然后使用反射动态创建对象。

事情是这样的:

private static JSONObject RULES; 
    static { 
     RULES= JSON.parse(rulesEngine.load()); 
    } 
    public class PersonFactory { 
     public Person create(int age) { 
     String personToCreate = RULES.get(age); 
     Constructor<?> ctor = Class.forName(personToCreate).getConstructor(); 
     return (Person) ctor.newInstance(); 
     } 
    } 

JSON的规则是这样的:

{ 
    "1":"Child.class", 
    "2":"Child.class", 
    ..., 
    "17":"Child.class", 
    "18":"Adult.class", 
    ..., 
    "69":"Adult.class", 
    "70":"Pensioner.class" 
} 

这样,你不打破OCP原则。

+0

你建议每次调用该方法时重新加载规则。我怀疑OP是否对运行时修改规则感兴趣;通过静态属性加载和解析一次就足够了。 – tucuxi

+0

是的,也许在运行时加载不好。但我的观点是不同的 - 我的观点是你可以动态地改变这些规则。 – alayor

+1

您已将“更新PersonFactory到最新版本”的负担转移到“将PersonFactory规则文件更新为最新版本”。 – tucuxi

2

开放封闭的原则很好记住。但是,它不能很好地与工厂合作。一个选择排序的,工作了下面,果然工厂到注册表:

PersonFactory pf = new PersonFactory(); 
    // Java 8 lambdas are great! 
    pf.register((age) -> age < 18 ? new Child() : null); 
    pf.register((age) -> age >= 18 ? new Adult() : null); 

    System.out.println(pf.create(10).getDescription());  

类似于@alayor的答案,以避免修改工厂的逻辑,或者具有唯一途径完全取代工厂,让每个人都使用新版本...是为了让工厂从别处获得逻辑。 @alayor从配置文件中获取它;我建议将它作为其初始化的一部分添加到工厂中(也可以在工厂构造函数中完成,例如将其更改为public PersonFactory(PersonCreator ... rules))。

全码:

interface PersonCreator { 
    Person create(int age); 
} 

class PersonFactory { 
    private List<PersonCreator> pcs = new ArrayList<>(); 

    public void register(PersonCreator pc) { 
     pcs.add(pc); 
    } 

    public Person create(int age) { 
     for (PersonCreator pc : pcs) { 
      Person p = pc.create(age); 
      if (p != null) { 
       return p; 
      } 
     } 
     return null; 
    } 
} 

interface Person { 
    public String getDescription(); 
} 

class Adult implements Person { 
    @Override 
    public String getDescription() { 
     return "I am an ADULT"; 
    } 
} 

class Child implements Person { 
    @Override 
    public String getDescription() { 
     return "I am a CHILD"; 
    } 
} 

public class Main { 
    public static void main(String[] args) { 
     PersonFactory pf = new PersonFactory(); 
     // Java 8 lambdas are great! 
     pf.register((age) -> age < 18 ? new Child() : null); 
     pf.register((age) -> age >= 18 ? new Adult() : null); 

     System.out.println(pf.create(10).getDescription());    
    } 
} 
+0

Downvoter - 谨慎评论为什么? – tucuxi

+0

我不是downvoter;然而,OCP与GoF工厂模式完美配合。在这个线程(和其他很多其他的)中的问题是,给出的例子是_不是GoF工厂模式。给出的例子通常被称为简单工厂,它_does_违反OCP,但要注意简单工厂与更有名的GoF模式无关。 – jaco0646

+0

我无法看到任何工厂如何避免与实际构建所需的构造函数耦合 - 除非使用依赖注入或注册表来代替。添加一个新类总是需要在工厂的某个地方创建一个新的构造函数。 GoF示例调用构造函数,因此不具有可扩展性。事实上,AFAIK是工厂的一个主要目标,就是将这个不可扩展点封装在一个位置。 – tucuxi