设计模式之简单工厂模式

释放双眼,带上耳机,听听看~!

今天我们聊一下设计模式中的简单工厂模式。一看这名字,第一个想法就是这种模式比较简单,的确简单工厂模式确实比较简单,我甚至觉的它是除了单例模式(该模式在之后的文章中在做介绍)之外最简单的一种设计模式了。但往往越简单的模式在实际的场景中用到的可能性就越高。既然已经学到设计模式了,这就说明,我们已经掌握了抽象、封装、继承和多态等特性,因为学习设计模式要用到这些知识。为了我们更好的介绍简单工厂模式,我们假设有下面这一个需求。

需求:老王开车去东北。

初步一看这样的需求很简单,只要我们抽象一个人或者一辆车都可以,然后我们在抽象出一个去东北的方法即可。下面我们按照这样的思路来设计我们相关的类。在这里我们将上面的需求抽象成一辆车,并添加go方法。

Car源码:

/**
 * 抽象一辆车
 */
public class Car {

    /**
     * 抽象去东北的方法
     */
    public void go() {
        System.out.println("开车去东北");
    }

}

Test源码:

/**
 * 测试类
 */
public class Test {
    public static void main(String[] args) {
        Car car = new Car();
        car.go();
    }
}

输出结果:

开车去东北

这样我们就实现了这个简单的需求了。但我们在设计任何系统时都要考虑一个很现实的问题就是需求变更,并且需求变更在任何一家公司都很常见,如果我们架构设计的比较好,在需求变更时我们修改的代码就会很越少,反之修改代码的代价就会很大。下面我们将上面的需求做一个简单的修改来看看我们怎么修改代码。

需求:老王可以选择任意的交通工具去东北。

我们还是向刚刚上一个需求一样,先抽象出类和方法。既然是任意的交通工具,那我们在按照Car的方法在抽象出一个火车的类,来模拟老王做火车去东北。下面为具体的源码:

Train 源码:

/**
 * 抽象一列火车
 */
public class Train {

    /**
     * 抽象去东北的方法
     */
    public void go() {
        System.out.println("坐火车去东北");
    }


}

Test源码:

/**
 * 测试类
 */
public class Test {
    public static void main(String[] args) {
//        Car car = new Car();
//        car.go();

        Train train = new Train();
        train.go();
    }
}

输出结果:

坐火车去东北

我们看结果虽然实现了所谓的需求,但我们知道这样的方式显然不是最好的,因为测试类中的原先写的代码基本上全都改了,如果我们此时要添加乘飞机去东北时,那我们测试类中的代码还要全部重新修改一次,这站在软件设计方面来看,这样的程序是不方便扩展的,扩展性差的程序,当遇到需求变更时,修改代码的难度也就越大。那有没有什么办法能够在我们不更改客户端代码或者修改比较少的时候满足上述的需求呢?答案一定是有的,也就是上面提到的多态。在Java中多态指的是同一个行为可以有多种不同的表现形式。在代码角度来说,也就是父类引用可以指向子类对象。如果我们将上述代码改成多态的形式,那么客户端代码在修改时,只需要修改等号右边的子类对象就可以了,父类对象及调用的方法则不需要改动,这相比之前修改的代码量来说,已经少了一部分了。因为要用到多态时,必须有一个前提条件就是继承,也就是子类继承父类,所以我们要抽象一个交通工具的父类,然后将Car和Train都继承该父类,并实现该类中的方法。下面我们看一下具体的代码:

Vehicle接口:

/**
 * 抽象交通工具接口
 */
public interface Vehicle {

  /**
   * 抽象去东北的方法
   */
  void go();
}

Car子类:

/**
 * 抽象一辆车
 */
public class Car implements Vehicle {

    /**
     * 抽象去东北的方法
     */
    public void go() {
        System.out.println("开车去东北");
    }


}

Train子类:

/**
 * 抽象一列火车
 */
public class Train implements Vehicle{

    /**
     * 抽象去东北的方法
     */
    public void go() {
        System.out.println("坐火车去东北");
    }


}

Test源码:

/**
 * 测试类
 */
public class Test {

  public static void main(String[] args) {
    Vehicle vehicle = new Train(); // new Car();
    vehicle.go();
  }
}

输出结果:

坐火车去东北

这样我们就将上述中的需求修改成了多态方式了,这样的好处就是我们客户端的代码修改的代码量减少了,如果以后有其它的交通方式,比如乘飞机去东北。那我们只要新建一个飞机类然后实现交通工具接口即可,客户端修改时只要修改等于右边的子类对象即可。那上面的代码有没有什么问题呢?有没有什么地方可以继续优化呢?答案还是肯定的。优化的地方,也就是等号的右边,也就是创建子类对象的地方。这时可能有人会想,这创建对象的地方有什么问题呢?用的就是Java提供了的new关键字啊,这块也没有什么可以优化的地方了啊。我们在语法上确实没有什么地方可以优化了。但在软件设计方面还是可以优化的。也就是说如果我们让客户端自己实例化对象时,那我们服务的提供者不方便控制对象的创建,因为客户端可以通过new关键字自己创建,这样当我们服务的提供方修改了某些代码时,客户端可能还会需要修改。所以为了保证创建对象的可控性我们会将创建对象的具体逻辑封装到服务的提供者中,然后对外暴露某些方法可以让客户端获取到当前对象,这样当服务提供者底层修改时,客户端的代码是一点都不需要改动的,这样也就保证了程序的可扩展性。按照这样的思路,我们创建两个工厂类,也就是CarFactory和TrainFactory。顾名思义这两个工厂类就是的作业就是创建相应的Car和Train对象的。下面我们看一下具体的代码:

CarFactory类:

public class CarFactory {

  public Car getCar() {
    return new Car();
  }
}

TrainFactory类:

public class TrainFactory {

  public Train getTrain() {
    return new Train();
  }
}

Test源码:

/**
 * 测试类
 */
public class Test {

  public static void main(String[] args) {

//    CarFactory carFactory = new CarFactory();
    TrainFactory trainFactory = new TrainFactory();

    Vehicle vehicle = trainFactory.getTrain();
    vehicle.go();
  }
}

输出结果:

坐火车去东北

看结果我们实现了我们想要的,也就是将创建对象的逻辑封装到了工厂类,需要对象时在从工厂中获取。但上面的代码我们好像有点似曾相识。没错,也就是我们第一个版本的那个代码。当我们需要更改交通工具时,创建工厂类的代码将要重写,除此之外vehicle子类对象的地方也要重写,因为不能保证不同的工厂获取对象方法是一样的。那怎么解决这样的问题呢?答案我想你一定猜到了,就是多态。我们将上面中工厂也用修改为多态方式。也就是在抽象出一个统一的工厂父类,然后让其它的工厂类实现这个父工厂接口,并实现父工厂中的定义的方法,这样就保证了不同工厂创建对象的方法不一致的问题。好废话不多说,我们直接看修改后的代码。

VehicleFactory接口:

/**
 * 抽象工厂父类
 */
public interface VehicleFactory {

  /**
   * 定义返回对象方法
   *
   * @return
   */
  Vehicle getInstance();
}

CarFactory类:

public class CarFactory implements VehicleFactory {

  public Vehicle getInstance() {
    return new Car();
  }
}

TrainFactory类:

public class TrainFactory implements VehicleFactory {

  public Vehicle getInstance() {
    return new Train();
  }
}

Test源码:

/**
 * 测试类
 */
public class Test {

  public static void main(String[] args) {

    VehicleFactory vehicleFactory = new TrainFactory(); // new CarFactory();

    Vehicle vehicle = vehicleFactory.getInstance();

    vehicle.go();
  }
}

输出结果:

坐火车去东北

这样我们的程序就是一个可扩展的程序了,例如如果需求改成老王乘飞机去东北。那我们只需要创建一个飞机的工厂并且实现VehicleFactory接口,然后将vehicleFactory工厂的引用修改为该飞机工厂的子类即可。这样我们客户端代码的修改量是最小的。下面为是乘飞机去东北的代码:

Airplane类:

/**
 * 抽象一架飞机
 */
public class Airplane implements Vehicle {

    /**
     * 抽象去东北的方法
     */
    public void go() {
        System.out.println("乘飞机去东北");
    }

}

AirplaneFactory类:

public class AirplaneFactory implements VehicleFactory {

  public Vehicle getInstance() {
    return new Airplane();
  }
}

Test源码:

/**
 * 测试类
 */
public class Test {

  public static void main(String[] args) {

    VehicleFactory vehicleFactory = new AirplaneFactory(); // new TrainFactory(); // new CarFactory();

    Vehicle vehicle = vehicleFactory.getInstance();

    vehicle.go();
  }
}

输出结果:

乘飞机去东北

上述内容的应用就是简单工厂模式,你看,简单吧,我们不经意间就学会了。下面是简单工厂模式的类图:

img

下面为项目源码:

源码下载

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧