设计模式---工厂模式

工厂模式

  1. 简单工厂模式

    案例:由厨师做菜为例子,厨师可以做肉和鱼,让顾客来进行点菜

    public interface Chef {
        public void cook();
    }
    
    public class Meet implements Chef{
    
        @Override
        public void cook() {
            System.out.println("做锅肉~");
        }
    }
    
    
    public class Fish implements Chef{
        @Override
        public void cook() {
            System.out.println("做盘鱼~");
        }
    }
    

    没用到工厂模式下顾客点菜:

    public class Customer {
        private int type;
    
        public static Chef getMenu(int type){
            if(type == 1){
                return new Meet();
            }else if(type == 2){
                return new Fish();
            }else {
                return null;
            }
        }
    }
    

    创建产品实例的方法在顾客类里面,这种方式虽然也能获取对应想要的菜,但是代码耦合性高,扩展性极其的差。试想一下,如果厨师学习到了新的菜品—蛋炒饭,那么我们就需要增加一个蛋炒饭类,并且在顾客代码中进行修改:

    public class Egg implements Chef{
        @Override
        public void cook() {
            System.out.println("我爱蛋炒饭~");
        }
    }
    
    public class Customer {
        private int type;
    
        public static Chef getMenu(int type){
            if(type == 1){
                return new Meet();
            }else if(type == 2){
                return new Fish();
            }else if(type == 3){
                return new Egg();
            }else {
                return null;
            }
        }
    }
    

    这就违反了开闭原则( 对扩展开放,对修改关闭),并且创建实例的方法可能会用到很多地方,修改量很大,比如现在把这个顾客类细分为男顾客、女顾客或者老人小孩等,这些类里面都用到了getMenu()方法,那么此时增加新菜品要修改量是不是就有点多了呢?结合实际生活也很好理解,想想自己去餐厅点菜时是直接去后厨和厨师点的吗?

    所以需要用到简单工厂模式,本例中就相当于服务员:

    public class Waiter {
        public Chef getMenu(int type){
            if(type == 1){
                return new Meet();
            }else if(type == 2){
                return new Fish();
            }else if(type == 3){
                return new Egg();
            }else {
                return null;
            }
        }
    }
    
    public class Customer {
        private Waiter waiter = new Waiter();
    
        public Chef getMenu(int type){
            return waiter.getMenu(type);
        }
    }
    

    此时再有新菜品增加只需要去修改一个工厂类的代码就可以了,但是仍然是违反开闭原则,于是有了工厂方法模式。

  2. 工厂方法模式

    工厂方法模式是把普通工厂就是把简单工厂中具体的工厂类,划分成两层:抽象工厂层+具体的工厂子类层

    public abstract class CookFactory {
        public abstract Chef createChef();
    }
    
    public class MeetChef extends CookFactory{
        @Override
        public Chef createChef() {
            return new Meet();
        }
    }
    
    public class FishChef extends CookFactory{
        @Override
        public Chef createChef() {
            return new Fish();
        }
    }
    

    相当于有一个烹饪工厂(抽象工厂),工厂里面有专门做肉的厨师(具体工厂),也有专门做鱼的厨师(具体工厂),而顾客只用告诉这个烹饪工厂自己要的是肉还是鱼,工厂就会返回对应菜品的厨师,然后厨师就开始做拿手菜。

    public class Customer {
        public static Chef getMenu(CookFactory cookFactory){
            return cookFactory.createChef();
        }
    
        public static void main(String[] args) {
            Chef meet = Customer.getMenu(new MeetChef());
            meet.cook();
            Chef fish = Customer.getMenu(new FishChef());
            fish.cook();
        }
    }
    
    

    工厂方法模式很好地解决了简单工厂模式违反开闭原则的问题,在这个模式下当有新的菜品增加进来时,只需要增加一个对应这个菜品的厨师即可,也就是具体工厂类:

    public class EggChef extends CookFactory{
        @Override
        public Chef createChef() {
            return new Egg();
        }
    }
    

    这不需要对任何之前已有的代码进行修改就能完成对新菜品的添加。

    这个模式的优点也显而易见

    • 符合开闭原则:
      • 新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可
      • 简单工厂模式需要修改工厂类的判断逻辑
    • 符合单一职责原则:
      • 每个具体工厂类只负责创建对应的产品
      • 简单工厂中的工厂类存在复杂的if-else的逻辑判断,工厂方法模式对应的厨师直接做对应的菜,不需要传递type来判断做什么菜

    同时也有美中不足

    • 添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销
    • 一个具体工厂只能创建一种具体产品
    • 虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类

    应用场景

    • 当一个类不知道它所需要的对象的类时在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可。
    • 当一个类希望通过其子类来指定创建对象时在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
    • 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。
3.抽象工厂模式

意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

主要解决:主要解决接口选择的问题。

何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。

如何解决:在一个产品族里面,定义多个产品。每个具体的工厂负责一个产品族。抽象工厂的返回值为最高级抽象产品。

实质:就是将工厂方法模式中的具体工厂类也变成了抽象工厂。这样一个MeetChef类就可以不只是只能做肉了,还可以在需要时增加新的菜品,而且不改变原有的代码。

案例:举买车的例子。某客户想要购买一辆车,他要联系4S店,首先得有4S店(抽象工厂)的电话。客户上网查询(建造工厂),发现了宝马4S店(具体工厂)的电话和奔驰4S店(具体工厂)的电话。客户拨通了宝马4S店的电话(获取具体工厂),发现目前店里可以提供(生产)多款车型(具体产品)供客户选择(BMW 320、BMW 530,BMW 740)。客户拨通了奔驰4S店的电话(获取具体工厂),发现目前店里可以提供(生产)多款车型(具体产品)供客户选择(BenzC200、BenzE300)。

实现

汽车类:

/**
 * 最高级抽象产品,用于抽象工厂的建造方法的返回值
 */
public abstract class Car
{
    abstract void drive();
}

宝马产品类:

/**
 * 抽象产品
 */
public abstract class BMWCar extends Car
{
}
/**
 * 具体产品BMW320
 */
public class BMW320 extends BMWCar
{
    public void drive()
    {
        System.out.println("BMW320,运动酷炫。");
    }
}
/**
 * 具体产品BMW530
 */
public class BMW530 extends BMWCar
{
    public void drive()
    {
        System.out.println("BMW530,时不我待。");
    }
}

奔驰产品类:

/**
 * 抽象产品
 */
public abstract class BenzCar extends Car
{
}
/**
 * 具体产品C200
 */
public class BenzC200 extends BenzCar
{
    public void drive()
    {
        System.out.println("BenzC200,实惠有面");
    }
}
/**
 * 具体产品E300
 */
public class BenzE300 extends BenzCar
{
    public void drive()
    {
        System.out.println("BenzE300,商务气派");
    }
}

工厂类:

/**
 * 宝马工厂,覆盖所有宝马车型的构造方法
 */
public class BMWFactory extends AbstractFactory
{
    public Car getCar(String type) throws ClassNotFoundException,
            IllegalAccessException, InstantiationException
    {
        Class cl = Class.forName(type);
        return (BMWCar)cl.newInstance();
    }
}
/**
 * 奔驰工厂,覆盖所有奔驰车型的构造方法
 */
public class BenzFactory extends AbstractFactory
{
    public Car getCar(String type) throws ClassNotFoundException,
            IllegalAccessException, InstantiationException
    {
        Class cl = Class.forName(type);
        return (BenzCar)cl.newInstance();
    }
}

抽象工厂类:

public abstract class AbstractFactory
{
    public abstract Car getCar(String type);
}

超级工厂类:

/**
 * 超级工厂类,建造工厂的工厂
 */
public class FactoryProducer
{
    public static AbstractFactory getFactory(String type)      
    {
        Class cl = Class.forName(type);
        System.out.println("创建工厂"+type);
        return (AbstractFactory)cl.newInstance();
    }
}

验证:

/**
 * 验证
 */
public class Demo
{
    public static void main(String[] args) throws IllegalAccessException, 
        InstantiationException, ClassNotFoundException
    {
        AbstractFactory abstractFactory = FactoryProducer.getFactory("BMWFactory");
        Car bmwCar = abstractFactory.getCar("BMW320");
        bmwCar.drive();
 
        Car bmwCar1 = abstractFactory.getCar("BMW530");
        bmwCar1.drive();
 
        Car bmwCar2 = abstractFactory.getCar("BMW740");
        bmwCar2.drive();
 
        AbstractFactory abstractFactory1 = FactoryProducer.getFactory("BenzFactory");
        Car benzCar = abstractFactory1.getCar("BenzC200");
        benzCar.drive();
 
        Car benzCar1 = abstractFactory1.getCar("BenzE300");
        benzCar1.drive();
    }
}

优点

  • 符合开闭原则
  • 克服了工厂方法模式下一个具体工厂只能创建一种具体产品的窘境,
  • 对于使用工厂方法的类,如果要更换另外一种产品,不需要修改实例化的具体工厂类
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值