简单工厂讲解

现在我们来学习简单工厂,简单工厂的定义和类型,他的定义非常简单,有一个工厂对象决定创建出哪一种产品类的实例,

关键字创建产品,他的类型属于创建型,但是不属于GOF23种设计模式当中,这里面我也要讲一下,因为抽象工厂和工厂方法,

里面都是由简单工厂一步一步演进,那我们讲完这个之后呢,也可以形成对比,这样对大家理解是非常有益处的

我们看一下简单工厂的应用场景,首先工厂类创建的对象比较少,这种场景适合简单工厂,还有客户类只知道传入工厂类的

参数,对于如何创建对象,也就是创建对象的具体逻辑呢,不关心,那简单工厂非常简单,一会领着大家来coding的时候,

相信小伙伴们一定会理解的

我们来看一下简单工厂的优点,只需要传入一个正确的参数,就可以获取你所需要的对象,而无需知道其创建细节,

那还有判断的逻辑,可以决定在哪个场景创建一个实例,客户端可以免除直接创建的责任,那这里所说的客户端,

包括客户端应用层,后续我们都会用Test测试类来描述,这里面只是生产产品,简单工厂通过这种做法,实现了责任的

分割,提供了专门的工厂类,去创建对象,甚至不需要知道产品的类名,只需要知道对应的参数是什么,参数和类名肯定是

参数更简单,第二个是不需要知道其创建细节,我只要告诉你我需要一杯咖啡,简单工厂直接返回咖啡就行了

我们看一下简单工厂有什么缺点,工厂类的职责相对过重,增加新的产品的时候,需要修改简单工厂类的判断逻辑,

违背了开闭原则,前面讲原则的时候都有说,这些原则不能全部遵守,也不能不遵守,这是一个平衡,要把握其中的度,

根据实际的业务模型,因为工厂类集中了创建逻辑,一旦出现问题,或者出现bug,也会增加系统中类的个数,增加了

系统的复杂度,和理解难度,一旦增加新的产品,我们是不得不修改工厂的逻辑的,那在产品类型非常多的时候,

整个工厂逻辑就会过于复杂,不利于维护和扩展,那简单工厂还有一个缺点,那后面抽象工厂中

接下来我们一起来coding,我们也会查看简单工厂的UML,同时也会对源码进行解析,包括JDK的源码,

会查看mybatis的源码,找到我们学习的设计模式,以设计模式的角度来对开源代码进行一个解析
我们现在来学习工厂的相关模式,我们先来了解简单工厂,简单工厂严格意义上来说,他并不属于GOF23种设计模式的一种,

这里边一定要注意,那简单工厂像是一个编码风格,和习惯,很多人会把简单工厂认为是工厂模式,那工厂关键字相关的设计模式

在GOF当中,是分为工厂方法和抽象工厂的,那我们看一下维基百科对于设计模式的介绍,这里有一个Patterns by Type,

按照类型进行分类的一个设计模式,首先看这里Creational,这里是有5个设计模式的,分别是抽象工厂,建造者,工厂方法,

原型模式,还有单例模式,而这里是没有简单工厂模式的,简单工厂我们也是要讲的,首先从简单工厂开始,我们学习并了解之后呢,

对于工厂方法和抽象工厂,我们理解起来也会更容易一些,那首先我们创建一个包,在这里我们就会学习Creational设计模式,

在这里注意一下,简单工厂不属于GOF设计模式当中的,但是我们把它单独创建一个包,为了分类更合理,把它也放到Creational里面,

那简单工厂非常简单,我们来看一下例子,我们在学习这里面所有的设计模式的时候,首先我们描述一个业务场景,例如网上有JAVA

的视频,Python的视频,前端的视频

现在Test里面生成了一个PathonVideo,然后这两个Video继承Video,然后我们回到Test里面

这个来看就是非常清晰了,原来这个应用层client客户端,直接依赖PathonVideo和JavaVideo,现在并不是,应用层Test

测试类,他只依赖VideoFactory,而VideoFactory,创建具体的,Java视频还是Pathon视频,具体的生产过程都在VideoFactory

里面,Test只管直接使用

应用层生产视频,应用层找工厂,我直接用就可以了,我会告诉你我要什么视频,然后VideoFactory给我生产,

那我们回到代码
package com.learn.design.pattern.creational.simplefactory;

/**
 * 具体的视频的属性
 * 时常
 * 我们在类中就不描述了
 * 我们有一个生产视频的方法
 * 而这个生产视频应该是一个抽象方法
 * 例如JAVA课程的视频
 * 和Python课程的视频
 * 生产过程会有点不一样
 * 
 * 
 * @author Leon.Sun
 *
 */
public abstract class Video {
	/**
	 * 所以我们现在写一个抽象方法
	 * 这个时候我们的类也要是抽象类才可以
	 * 因为拥有了抽象方法
	 * 这个类必然是抽象类
	 * 
	 * 
	 */
    public abstract void produce();

}
package com.learn.design.pattern.creational.simplefactory;

/**
 * 紧接着我们创建一个JAVA的视频实现类
 * 他继承Video这个抽象类
 * 同时实现produce方法
 * 现在我们填充这个实现
 * 
 * 
 * @author Leon.Sun
 *
 */
public class JavaVideo extends Video {
    @Override
    public void produce() {
        System.out.println("录制Java课程视频");
    }
}
package com.learn.design.pattern.creational.simplefactory;

/**
 * PythonVideo他也实现Vedio
 * 实现方法
 * 
 * @author Leon.Sun
 *
 */
public class PythonVideo extends Video {
    @Override
    public void produce() {
        System.out.println("录制Python课程视频");
    }
}
package com.learn.design.pattern.creational.simplefactory;

/**
 * 
 * @author Leon.Sun
 *
 */
public class VideoFactory {

	/**
	 * 这里面我们使用Class
	 * 
	 * 这个class
	 * 就是包名
	 * 后面是一个类
	 * 
	 * 
	 * @param c
	 * @return
	 */
    public Video getVideo(Class c){
    	/**
    	 * 这里声明Video等于null
    	 * 后面会说明为什么要什么Video等于null
    	 * 
    	 * 
    	 */
        Video video = null;
        try {
        	/**
        	 * c.getName()获取类名
        	 * 这里面还需要一个强转
        	 * c.getName()是一个全的
        	 * 包括包名和类名
        	 * 然后获取他的一个对象
        	 * 把它强转成父类
        	 * 即使我们传JavaVideo或者PathonVideo
        	 * 都可以强转成父类的
        	 * 所以这里面Video也就赋值成功了
        	 * 
        	 * 然后调用newInstance方法
        	 * 来获取这个对象
        	 * 然后强转赋值给Video
        	 * 
        	 * 
        	 */
            video = (Video) Class.forName(c.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        /**
         * 下边我们直接返回Video就可以了
         * 
         */
        return video;
    }

	/**
	 * 首先我们要创建一个方法
	 * 返回值是Video
	 * 我们还要传一个类型type
	 * 根据type判断返回什么样的子类实现
	 * 那这个也比较简单
	 * 我们直接写
	 * 
	 * 我们看一下工厂的具体实现
	 * 现在我们要加一个算法的课程
	 * 或者前端的课程
	 * 我们要修改这个类的一个实现
	 * 里面创建一个具体的
	 * 前端课程的一个value
	 * 也就是说随着课程的一个扩展
	 * 我们的类要不断地修改
	 * 而修改就会带来风险
	 * 通过修改这个类
	 * 来扩展具体的一个类型
	 * 所以他并不符合开闭原则
	 * 那我们再来看一下UML
	 * 我们可以想象一下我们的目的是为了扩展
	 * 那是为了对扩展开放
	 * 和对修改关闭
	 * 这个时候我们就可以使用工厂方法来演进一下
	 * 我们再理会一下他们之间的差异
	 * 之后我们再像抽象工厂演进
	 * 然后再体会他们之间的不同
	 * 那通过这个我们加深对工厂模式的理解
	 * 也有把这个方法写成静态方法的
	 * 通过静态方法来建一个简单的工厂
	 * 这个也是比较常见的
	 * 使用静态方法就直接调用就可以了
	 * 不用创建VideoFactory
	 * 因为这个方法是静态方法呢
	 * 我们是无法改变这个行为
	 * 如果没有被继承或者重写的
	 * 直接使用VideoFactory是OK的
	 * 刚刚我们对简单工厂也分析了一下
	 * 对于简单工厂我们还可以通过反射
	 * 来弥补简单工厂的扩展性
	 * 利用反射来演示一下简单工厂
	 * 
	 * 
	 * 
	 * 
	 * @param c
	 * @return
	 */
    public Video getVideo(String type){
    	/**
    	 * 不区分大小写
    	 * 这里使用switch也是OK的
    	 * 
    	 * 这个时候判断他是一个Java视频
    	 * 
    	 */
        if("java".equalsIgnoreCase(type)){
        	/**
        	 * 直接返回一个Java视频
        	 * 
        	 */
            return new JavaVideo();
        }else if("python".equalsIgnoreCase(type)){
            return new PythonVideo();
        }
        return null;
    }

}
package com.learn.design.pattern.creational.simplefactory;

/**
 * 对于Test这个类它是属于应用层的
 * 我们也可以把它理解成调用类的一个client
 * 所以我们接下来讲应用层代码
 * 无论这个类是Test还是Client
 * 在我们这里面都是一样的
 * 我们创建一个Java的视频
 * new一个JavaVideo
 * 调用它的produce方法
 * 如果我们需要JAVA的视频
 * 那现在就是一个父类的引用
 * 指向了子类的一个实现
 * 在我们这个客户端类
 * Test类里面
 * 也就是说应用层类
 * 它是非常依赖JavaVideo这个类的
 * 例如我现在想生成PythonVideo
 * 这个类肯定是需要改的
 * 那现在我们在同一个包下
 * 所以不需要额外的import
 * 所以在import这个区域
 * 体现不出来的
 * 如果我们是跨包的
 * 或者是需要import的时候
 * 就可以看到是需要依赖的类的
 * 那我们能不能不让我们的应用层依赖对应的类
 * 那我们能够把生产过程放到一个类里面
 * 是我们的应用层不依赖于对应的具体实现类
 * 这个就是一个简单工厂的一个由来
 * 现在我们就把创建具体的视频过程移植到简单工厂里面
 * 那我们现在来创建这个类
 * 
 * 简单工厂有没有什么缺点
 * 
 * 
 * 
 * @author Leon.Sun
 *
 */
public class Test {
    public static void main(String[] args) {
    	/**
    	 * 现在就不直接new PathonVideo了
    	 * 我不需要直接依赖这个类
    	 * 那这里面也就不使用匿名对象了
    	 * 直接再new一个出来
    	 * 
    	 * 这里面并没有具体的PathonVideo还是JavaVideo
    	 * 如果导包的时候
    	 * 不需要导具体的
    	 * JavaVideo还是PythonVideo
    	 * 我们只导入一个VideoFactory就可以了
    	 * 现在他们都是同包的
    	 * 不需要import也能够拿到它
    	 * 现在看一下最新的UML类图
    	 * 
    	 * 首先new了一个VideoFactory
    	 * 这个毋庸置疑
    	 * 
    	 */
//        VideoFactory videoFactory = new VideoFactory();
    	  /**
    	   * 通过videoFactory的getVideo方法
    	   * 里面传一个JAVA
    	   * 
    	   * 然后进入getVideo方法
    	   * 
    	   * 这个时候注意Video的一个类型
    	   * 具体的指向的是JavaVideo的一个对象
    	   * 这个对象的具体类型是JavaVideo类型
    	   * 同时它也是Video类型
    	   * Video instanceof JavaVideo
    	   * 但是他不是PathonVideo类型
    	   * Video instanceof PathonVideo
    	   * 然后继续
    	   * 
    	   * 
    	   */
//        Video video = videoFactory.getVideo("java");
    	  /**
    	   * 如果为空就直接return
    	   * 
    	   */
//        if(video == null){
//            return;
//        }
    	  /**
    	   * 然后这里直接调用JavaVideo的produce方法
    	   * 这个就是简单工厂
    	   * 非常容易理解
    	   * 
    	   * 
    	   */
//        video.produce();

        VideoFactory videoFactory = new VideoFactory();
        /**
         * 这里要传对应的class
         * 那我们只需要传JavaVideo.class就可以了
         * 这样有一个扩展性
         * 从一定程度上满足开闭原则
         * 如果我们新增一个前端课程
         * FEVideo
         * 那只需要传FEVideo的类就可以了
         * 而这个工厂是不需要变化的
         * 工厂我们不需要再修改他
         * 我们现在来debug一下
         * 
         * 
         */
        Video video = videoFactory.getVideo(JavaVideo.class);
        if(video == null){
            return;
        }
        /**
         * 直接执行produce方法
         * 
         */
        video.produce();



    }

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值