简单工厂
为什么要用工厂模式?
因为,基于面向接口编程的思想,程序开发者,往往只会向程序的使用者提供接口,这个接口,是指广泛意义上的接口,就是开发者提供给你的入口,不单单指Java中的接口。因此,我们在开发时,用到的各种jar包,其实就是class文件,我们往往是拿不到源代码的,也不能修改源代码,因此,我们是不直到源代码的实现细节的。
就像下面的代码,分割线以上,是开发者提供给你的接口及具体实现,分割线以下,是使用者,使用者使用时,是不能直接new开发者的Hamburger对象的,首先,使用者就应该不知道具体实现细节,其次,如果Hamburger改成Hamburger2的话,使用者拿到的是class文件,肯定不知道,就会报错。说白了,就是会违反迪米特原则:不能知道别人内部实现细节。
interface Food{
void eat();
}
class Hamburger implements Food{
@Override
public void eat() {
System.out.println("吃汉堡");
}
}
//===============================================================================
public class Test {
public static void main(String[] args) {
Food f =new Hamburger();
f.eat();
}
因此,引出了简单工程模式,就是用来解决上述问题的。
此时,使用者只需要通过调用工厂方法,然后输入1或者2就行了,此时,就算源代码修改实现类的名字,工厂内的名字会改变,但是,工厂类也是在源代码中的,因此,对使用者来说,看不到改变,不违反迪米特法则。
简单工厂缺点:
如果变化来了,需要扩展产品时,就必须要修改工厂中代码,这样便违反了开闭原则。比如,简单工厂和几个实现类都是前人写好的,然后那人跑路了,现在工作交给你了,现在需要你添加几个新food,基于开闭原则,你是不应该修改前人的工厂代码的。更或者,你手中只有class文件,没有前人的工厂代码,添加新功能,必须修改工厂细节,简单工厂就不行了。
interface Food{
void eat();
}
class Hamburger implements Food{
@Override
public void eat() {
System.out.println("吃汉堡");
}
}
class Noodle implements Food{
@Override
public void eat() {
System.out.println("吃面条");
}
}
class FoodFactory{
public static Food getFood(int n){
switch (n){
case 1:
return new Hamburger();
case 2:
return new Noodle();
}
return null;
}
}
//===============================================================================
public class Test {
public static void main(String[] args) {
Food f =FoodFactory.getFood(2);
f.eat();
}
工厂方法
简单工厂解决了使用者能直接操作具体实现对象,违反迪米特法则的问题。但是,带来的问题的,工厂类本身需要频繁修改,如果工厂是class文件呢?违反开闭原则。那么,工厂类其实也就不能直接写死,对吧?因此,我们把工厂类写成一个接口,每次需要添加功能时,添加一个实现类,同时,可以再添加一个工厂实现类,就解决了。
个人疑问:
那么,工厂方法是不是又犯了和不适用工厂时一样的错误呢?我们不能知道开发者提供的具体实现类,直接new就违反迪米特法则了。那么,我们在这里直接new工厂的实现类,不也是一样的嘛?照样违反迪米特法则。
其实,确实是这么回事,不过,目的不同,具体实现类,是封装后不希望使用者知道细节的,但是,工厂的实现类,是希望使用者去了解使用的,因此,目的不同,开发者会主动提供工厂实现类来给使用者使用,此时,开发者提供的内容,其实就是广义接口的含义,因此,此时的工厂实现类,属于开发者主动提供的,属于接口,因此,不是使用者私自调用的,不违法迪米特法则。
缺点:
如果有多个产品等级,那么类的数量就会爆炸增长。多一个产品,就会多一个工厂接口及其各种实现类工厂。比如,食物需要食物工厂,饮料需要饮料工厂,再多别的,就需要别的其他工厂,到处都是工厂。
interface Food{
void eat();
}
class Hamburger implements Food{
@Override
public void eat() {
System.out.println("吃汉堡");
}
}
class Noodle implements Food{
@Override
public void eat() {
System.out.println("吃面条");
}
}
interface FoodFactory{
public Food getFood();
}
class HamburgerFactory implements FoodFactory{
@Override
public Food getFood() {
return new Hamburger();
}
}
//===============================================================================
public class Test {
public static void main(String[] args) {
Food f =new HamburgerFactory().getFood();
f.eat();
}
抽象工厂
如果有多个产品等级,那么工厂方法类的数量就会爆炸增长。因此,用抽象工厂解决这个问题。
工厂方法相当于,把一个产品等级抽象成一个具体类别,也就是说,工厂方法的接口其实还是代表某一个工厂的,食物工厂的接口,就只能生成食物。
而抽象工厂的思想,就是工厂生产的内容,其实是可以自己随便组合的,工厂方法规定某种工厂只能生产某种类型,是自己把路走窄了。因此,抽象工厂相当于,就是抽象出一个组合关系,然后,抽象工厂的实现类中,就可以自由组合各种生成类型,本例中,就是可以组合食物接口和饮料接口。相当于,总的抽象工厂就是个组合框架,框架中写组合什么功能的接口,然后,抽象工厂的具体实现中写各个接口的具体实现。
缺点:
当产品等级发生变化时,都要引起以前工厂的修改。比如,此题中,如果多一个产品等级,目前生成食物和饮料,如果突然多一个生产水果,就需要修改抽象工厂了,需要多加一个水果的接口。
interface Food{
void eat();
}
interface Drink{
void drink();
}
class Cola implements Drink{
@Override
public void drink() {
System.out.println("喝可乐");
}
}
class Hamburger implements Food{
@Override
public void eat() {
System.out.println("吃汉堡");
}
}
class Noodle implements Food{
@Override
public void eat() {
System.out.println("吃面条");
}
}
interface Factory{
public Food getFood();
public Drink getDrink();
}
class FoodFactory implements Factory{
@Override
public Food getFood() {
return new Hamburger();
}
@Override
public Drink getDrink() {
return new Cola();
}
}
//===============================================================================
public class Test {
public static void main(String[] args) {
Food f =new FoodFactory().getFood();
f.eat();
Drink d =new FoodFactory().getDrink();
d.drink();
}
总结
相当于,工厂方法中,多一个产品等级,多一个水果工厂,就是多写一个接口的问题,但是没办法自由组合。而抽象工厂,就是随便组合,比如,生产食物,或者食物和饮料都生成,都行,但是,如果此时多一个产品等级水果,之前的组合可能就需要改动了,此时,就违反开闭原则了。