zoukankan      html  css  js  c++  java
  • 面试官:小伙子,够了够了,一个工厂模式你都在这说半个小时了!

    前言

    创建型模式、主要用于解决Java对象的创建问题

    工厂模式

    工厂模式的说明

    在面向对象的编程中,继承和多态的概念实现了父类与子类之间的(is-A)关系

    基于这种关系实现了子类的多态性,这种抽象的方式为子类提供了相同的操作并且支持子类的功能性拓展。但是出现了这样的问题?

    Verhicle verhicle = new Car();

    Verhicle verhicle = new Truck();

    problem所在

    1. 子类复写了父类的方法,那么子类实例化父类对象时,就必须每一个子类都要进行。这样就造成了对于不同的子类要调用不同的构造器去实例化父类对象。缺乏统一性操作
    2. 另外从上面的两行代码可以看出子类与父类之间的依赖关系、耦合度高
    3. 违反了父类的开闭原则子类的单一职责原则

    简单工厂模式的引入,实现逻辑的封装,使用公共的工厂类实现统一创建对象实例化父类的行为。

    简单工厂模式

    • 简单工厂实现的三种方式
      1. 静态工厂模式
      2. 使用反射机制进行类注册
      3. 使用newInstance方法进行类注册

    简单工厂的UML图

    • 静态工厂模式的解决方式

    创建一个单独的verhicle简单工厂类。通过内置枚举储存所有需要创建实例的子类,并通过统一的create(type)方法根据传入参数的类型实现按需创建实例。

    public class VerhicleFactory {
        public enum VerhicleType {
            Car,Truck,Boat;
        }
    
        public static Verhicle create(VerhicleType type) {
            if(type.equals(VerhicleType.Car)) {
                return new Car();
            }
            if(type.equals(VerhicleType.Truck)) {
                return new Truck();
            }
            if(type.equals(VerhicleType.Boat)) {
                return new Boat();
            }
            else return null;
        }
    }
    
    

    优势

    这种额外使用工厂类的方式,解决了上面子类实例化父类的破坏单一职责原则、实现了构造实例的统一性操作。

    缺点

    可以从存储的枚举看出,一旦新增拓展的子类就必须修改工厂的枚举,破坏了工厂类自身的开闭原则。

    仍然没有解决父类的对内关闭的对外拓展的开闭原则。

    • 使用反射机制进行类注册的解决方式

    为了解决静态工厂模式破坏自身开闭原则的弊端、我们可以使用反射机制使得注册的新类在使用时被实例化。从而保证了对外拓展开发,对内修改闭合。也就是说即使新增对父类拓展的子类,也不再重新修改静态工厂内的枚举。

    //服务端
    
    public class Product {/*类体内容*/}
    public class Product1 extends Product {}
    /*...更多的拓展子类...*/
    public class MoreProducts extends Product {}
    
    /*使用反射机制进行类注册的简单工厂模式*/
    
    private Map<String,Class> registeredProduct = new 
        HashMap<String,Class>();
    
    /**实现对拓展子类的注册、所有的拓展子类将会被记录在Map集合中
     *@parameter
     * productId子类ID也就是子类的类型,对应静态工厂的枚举类型
     * productClass子类的类对象,也就是子类的Class对象
     */
    public void registerProduct(String productId, Class productClass) {
        registeredProduct.put(productId, productClass);
    }
    
    /**更具传入的子类类型、构造对应的子类实例并返回
     *@parameter
     * ProductType子类类型和上面的子类ID一致,对应静态工厂的枚举类型
     */
    public Product createProduct(String ProductType) throws InstantiationException,IllegalAccessException {
        Class productClass = registeredProduct.get(ProductType);
        return (Product) productClass.newInstance();
    }
    
    //客户端就依据相应的方法,进行类的注册和实例创建
    
    

    优点

    解决了静态工厂类破坏开闭原则的弊端,把注册和创建实例分开实现注册的类在使用时才实例化。

    缺点

    反射机制影响了程序性能、使用多了的话,程序性能肯定要低效很多。

    • 使用newInstance方法进行类注册的简单工厂模式

    只是基于上面反射机制进行类注册的思想进行了一个小的修改

    1. 避免使用反射机制、提高性能
    2. 如何实现呢?
      • Map集合中不在存储Class对象,而是已经创建的子类实例
    • 基类中创建一个抽象方法
    • 子类全部复写、方法体是创建子类对象
      • 这样就可以实现上面Map集合存储的时已经创建的子类实例
    //服务端
    
    public abstract class Product {
        Product newInstance();
    }
    public class Product1 extends Product {}
    /*...更多的拓展子类...*/
    public class MoreProducts extends Product {
        @override
        public  MoreProducts newInstance() {
            return new MoreProducts();
        }
    }
    
    /*使用反射机制进行类注册的简单工厂模式*/
    
    private Map<String,Product> registeredProduct = new 
        HashMap<String,Product>();
    
    /**实现对拓展子类的注册、所有的拓展子类将会被记录在Map集合中
     *@parameter
     * productId子类ID也就是子类的类型,对应静态工厂的枚举类型
     * productClass子类的类对象,也就是子类的Class对象
     */
    public void registerProduct(String productId, Product productType) {
        registeredProduct.put(productId, productType);
    }
    
    /**更具传入的子类类型、构造对应的子类实例并返回
     *@parameter
     * ProductType子类类型和上面的子类ID一致,对应静态工厂的枚举类型
     */
    public Product createProduct(String ProductId) throws InstantiationException,IllegalAccessException {
        Product productType  = registeredProduct.get(ProductId);
        return (Product) productType.newInstance();
    }
    
    //客户端就依据相应的方法,进行类的注册和实例创建
    
    

    优点

    把基类之间划为抽象类,解决了父类的开闭原则和子类的单一职责原则。支持了创建对象的统一性操作。

    缺点

    类之间的继承关系造成代码依然耦合度高的问题。

    仍然存在未实现父类对内修改闭合的风险。

    工厂方法模式

    工厂方法模式的UML图

    image

    解决方式:

    1. 服务端把产品接口化、对于所有需要拓展的产品可以直接实现统一接口方法,以及自定义方法。

    2. 另外抽象化工厂、可以将产品的服务化功能集成在抽象工厂,并且内置构造实例的抽象方法。那么所有的拓展产品的工厂类都可以继承实现对应的构造方法和功能。

    //  sever
    public interface Product{
        void features();      //定义统一的功能
    }
    public class Product1 implements Product {/*实现统一接口方法、以及自定义方法*/}
    public class MoreProducts implements Product {/*实现统一接口方法、以及自定义方法*/}
    
    /*工厂方法*/
    
    public abstract class AbastractFactory {
        Product create();        //定义统一构造器
        void sever(){/*相应的统一服务功能*/}
    }
    public class Product1Factory implements AbstractFactory {/*实现构造器*/}
    public class MoreProductsFactory extends AbstractFactory {/*实现构造器*/}
    
    

    优势:

    将产品解耦,并且每一个拓展产品都有相应的工厂类实现对应的构造实例以及服务功能

    缺点:

    易造成工厂的冗余、拓展产品过多时出现工厂类增多。

    抽象工厂模式

    解决方法:

    在工厂方法模式的基础之上、在抽象工厂中不再单独创建统一的构造器,而是创建一类或者所有的拓展产品构造器。这样工厂类继承的时候只需要少了的继承就可以完成多个拓展产品的构造任务。

    //  sever
    public interface Product{
        void features();     //定义统一的功能
    }
    public class Product1 implements Product {/*实现统一接口方法、以及自定义方法*/}
    public class MoreProducts implements Product {/*实现统一接口方法、以及自定义方法*/}
    
    /*抽象工厂模式*/
    
    public abstract class AbastractFactory {
        Product createProduct1();        //定义Product1构造器
        Product createMoreProducts();    //定义MoreProducts构造器
        void sever(){/*相应的统一服务功能*/}
    }
    
    public class ProductFactory extends AbstractFactory {/*实现对应构造器*/}
    
    

    优点:

    减少了工厂类的数量、避免冗余问题

    最后

    欢迎关注公众号:前程有光,领取一线大厂Java面试题总结+各知识点学习思维导+一份300页pdf文档的Java核心知识点总结!

  • 相关阅读:
    webpack学习遇到大坑(纯属自己记录)
    git忽略某些文件提交
    数据结构(一)创建并遍历线性列表
    数据结构二 顺序表的创建
    JqGrid动态改变列名
    构造DataTable
    计算机存储数据的单位
    .NET Core在WindowsServer服务器部署(使用Web Deploy发布)
    mysql ERROR 1045 (28000): 错误解决办法
    ASP.NET取得Request URL的各个部分
  • 原文地址:https://www.cnblogs.com/lwh1019/p/14198780.html
Copyright © 2011-2022 走看看