设计模式学习(三):工厂模式

jkouu 77 0

顾名思义,工厂模式的作用就是用来创建一系列实例。如果说建造者模式是把实现一种功能的过程进行封装,让用户只关心功能能否实现,而不关心功能如何实现的话,工厂模式就是把创建实例的过程进行封装,让用户只关心能否创建需要的实例,而不关心如何创建实例。

换句话来说,工厂模式实现了实例创建与实例使用的解耦。

工厂模式的实现有三种:简单工厂,抽象工厂和工厂方法。下面我们依次来看一下这三种实现。

1.简单工厂

我们看一个简单的情景:

我们都玩过著名的FPS游戏:CSGO。在游戏中,CT可以使用这三种武器:USP,UMP-45和M4A4。游戏开始前,我们可以购买相应的武器并使用。

如何用简单工厂来实现这个情景呢?

1.1.创建抽象类

首先,我们要创建一个抽象类,也就是枪类。

public abstract class Gun{
    public abstract void fire();
}

内容很简单,所有的枪都可以开火对吧?

1.2.实现子类

下面我们来创建三种枪作为枪的子类。

public class USP{
    @Override
    public void fire(){
        System.out.println("USP开火");
    }
}

public class UMP45{
    @Override
    public void fire(){
        System.out.println("UMP-45开火");
    }
}

public class M4A4{
    @Override
    public void fire(){
        System.out.println("M4A4开火");
    }
}

1.3.创建工厂类

接下来,我们来创建生产枪的工厂类:

public class GunFactory{
    public static Gun getWeapon(String type){
        switch(type){
            case "USP":
                return new USP();
            case "UMP-45":
                return new UMP45();
            case "M4A4":
                return new M4A4();
           default:
           return null;             
        }
    }
}

写到这里,简单工厂就创建好了。

1.4.使用

接下来我们就可以在程序里购买枪械了。

public static void main(String[] args){
    Gun usp = GunFactory.getWeapon("USP");
    usp.fire();
    
    Gun ump = GunFactory.getWeapon("UMP-45");
    ump.fire();
    
    Gun m4a4 = GunFactory.getWeapon("M4A4");
    m4a4.fire();
}

1.5.评价

可以看到,简单工厂的实现,实际上就是实现了一个静态的方法,所以简单工厂又被称为静态工厂。

简单工厂实现了创建实例与使用实例的解耦,用户不用关心实例如何被创建,只需要去使用实例就可以了。

当然,简单工厂也有很多局限性。首先,它集中了所有产品类的创建逻辑,如果工厂的逻辑出现了错误,那么整个程序都会受到影响;其次,它并不适用“开放-关闭”原则,因为每添加一种产品类,都要再对工厂进行改动,这会导致工厂的代码越来越庞大;最后,因为简单工厂是通过静态方法来实现的,它不能被继承,扩展性也比较差。

2.工厂方法

工厂方法是对简单工厂的一个改良,它解决了简单工厂不适用“开放-关闭”原则的缺点。我们还以上面的情景作为例子来看一下工厂方法的实现。

2.1.创建抽象产品类和实现产品子类

这一步骤的实现和简单工厂一样,这里就不再重复写一遍代码了。

2.2.创建抽象工厂类

与简单工厂不同的是,在工厂方法的实现中,我们对工厂也做了抽象:

public abstract class AbstractGunFactory{
    public abstract Gun getWeapon();
}

2.3.实现工厂子类

既然有了抽象,我们当然要实现具体的工厂类了。

public class UspFactory extends AbstractGunFactory{
    public Gun getWeapon(){
        return new USP();
    }
}

public class UmpFactory extends AbstractGunFactory{
    public Gun getWeapon(){
        return new UMP45();
    }
}

public class M4A4Factory extends AbstractGunFactory{
    public Gun getWeapon(){
        return new M4A4();
    }
}

2.4.使用

接下来我们看一看工厂方法的使用:

public static void main(String[] args){
    AbstractGunFactory uspFactory = new UspFactory();
    Gun usp = uspFactory.getWeapon();
    usp.fire();
    
    AbstractGunFactory umpFactory = new UmpFactory();
    Gun ump = umpFactory.getWeapon();
    ump.fire();
    
    AbstractGunFactory m4a4Factory = new M4A4Factory();
    Gun m4a4 = m4a4Factory.getWeapon();
    m4a4.fire();
}

可以看到,我们是通过创建对应枪械的工厂实例来获取枪械的。实际上,我们在获取工厂实例时,还可以使用单例,这样从逻辑上来看是最合理的。

2.5.评价

工厂方法模式虽然适用了”开放-关闭“原则,但是也增加了新的开销。当我们需要再增加一种新的可购买枪械时,虽然我们不用对工厂的逻辑进行改动,但是我们却还要再实现一种新的工厂,这会导致程序中类的数量大大增加。除此之外,一个工厂只能创建一种产品,这也实在不能称之为工厂呀。

3.抽象工厂

为了显示抽象工厂的作用,我们再将情景复杂化。

在CSGO中,有CT和T两个阵容,每个阵容都可以购买手枪,冲锋枪和步枪,但是各自阵容能购买的枪械却不一样。对于CT来说,手枪只能购买FN57,冲锋枪只能购买MP9,步枪只能购买M4A4;对于T来说,手枪只能购买Tec-9,冲锋枪只能购买Mac-10,步枪只能购买AK-47。

显然,在这种情景下,简单工厂和工厂方法都不能很好地去实现,这时我们就用到了抽象工厂的方法。

3.1.创建产品抽象类和实现产品子类

在这个情境下,我们涉及到了六种枪械,因为枪械的实现都很简单,所以这里我就不写了,大家知道什么意思就行。

3.2.创建抽象工厂

首先,我们还是要创建抽象工厂。

public abstract class GunFactory{
    public abstract Gun getPistol();
    
    public abstract Gun getSmg();
    
    public abstract Gun getAr();
}

在这个抽象工厂中,我们定义了购买手枪的getPistol()方法,购买冲锋枪的getSmg()方法和购买步枪的getAr()方法。

3.3.实现工厂子类

因为T和CT都可以购买这三类枪,所以我们需要实现两个阵营的工厂。

public class CtGunFactory{
    public Gun getPistol(){
        return new FN57();
    }
    
    public Gun getSmg(){
        return new MP9();
    }
    
    public Gun getAr(){
        return new M4A4();
    }
}

public class TGunFactory{
    public Gun getPistol(){
        return new Tec9();
    }
    
    public Gun getSmg(){
        return new Mac10();
    }
    
    public Gun getAr(){
        return new AK47();
    }
}

3.4.使用

接下来我们看一看抽象工厂的使用。

public static void main(String[] args){
    GunFactory ctFactory = new CtGunFactory();
    GunFactory TFactory = new TGunFactory();
    
    Gun fn57 = ctFactory.getPistol();
    fn57.fire();
    
    Gun mp9 = ctFactory.getSmg();
    mp9.fire();
    
    Gun m4a4 = ctFactory.getAr();
    m4a4.fire();
    
    Gun tec9 = tFactory.getPistol();
    tec9.fire();
    
    Gun mac10 = tFactory,getSmg();
    mac10.fire();
    
    Gun ak47 = tFactory.getAr();
    ak47.fire();
}

在使用上,抽象工厂和工厂方法一样,通过工厂的实例来获取产品。当然,我们也可以通过单例模式让工厂在逻辑上更合理。

3.5.评价

抽象工厂模式的思考角度与前面两种实现完全不同。简单工厂和工厂方法两种实现都是从产品类别的角度去思考的,抽象工厂则是从工厂类别的角度去思考的。独特的思考角度,决定了抽象工厂在复杂情景下能发挥出更好的作用。但是抽象工厂也有一个缺点:对新产品族工厂开放,对新产品关闭。

比如在上面的情景下,如果再增加一个阵营,我们只需要再新实现一个工厂类就可以了。但是如果再增加一种枪械,比如机枪,那么就需要再对工厂进行修改了。

 

 

发表评论 取消回复
您必须 [登录] 才能发表评论!
分享