如何从Java接口的角度切入静态工厂模式

面向接口编程

接口的定义及功能

这里从java介入吧,在java中,接口是一种特殊的类,接口里面的量都是常量,接口的方法只有定义而没有实现,换句话说,接口就像一个菜单,它只会告知你我有什么菜,而并不会有实际的菜品,所以通常用接口来定义实现类的外观,根据外部应用所需要的功能,约定实现类的能力(类的功能不仅限于接口约束)。通过接口,可以实现不相关类的相同功能,而不考虑这些类之间的层次关系,接口就是实现类对外的外观。

上面那样说,可能显得很装13,那千言万语化成一句人话就是:1、定义功能,对外暴露 2、对内约束实现类的行为

面向接口编程的意义

所谓面向接口去编程的核心含义就是为了——“封装隔离”

通常的封装,是指对数据结构的封装,将几种数据类型整到一块,组成一个新的数据类型;而java中的封装,包含对数据和行为进行抽象,然后定义出一个类,这个类即封装了数据和行为,但接口这里的封装,更多的是指对于行为(能力、方法)的封装,是一种“对被隔离体能力的封装”,而隔离对应的就是,外部的调用以及内部的实现,外部只根据接口来调用方法(根据菜单来点菜,具体填饱肚子的菜是内部去做),外部调用是不知道内部你是用什么方式实现的,举个例子,就像我有一个计算器,计算器的加减乘除按键就是我提供给用户的接口,用户只知道我有加减乘除的能力,但当他用乘法按键去运算的时候,后台具体是用二进制运算,还是逐个数累加或者其他什么方式来完成这个乘法功能,用户是不知道的。也就是外部调用和内部实现是被隔离开的。

既然外部调用和内部实现被隔离开了,那么只要接口不变,内部实现怎么变化都不会影响外部应用对这个接口的调用,从而让系统更加的灵活,更便于扩展和维护,也就是传说中的“接口是系统可插拔的保证”。

说到这里插一段题外话,emmm……个人感觉编程是一个人的事,很多时候1+1<2,因为人这个不可控因素,每个程序员的思想深度,技术水平,都是不相同的,所以往往会出现 “ 一个程序员A费劲心力,设计了面向对象的模块化代码结构,并完成了一部分功能,而后面有别的需求介入,另一个的程序员B加入了研发过程,基于这个代码进行改动的时候,并读不懂A的结构和A事先预留的扩展方式,直接用他的方式去硬编码,强行破坏了整个结构”。以上这种情况往往很令人崩溃,所以对于水平参差不齐的团队来说,集体劳作的质量(单指代码)并不那么友好

总之,在开发中,优先选择使用接口,在即要定义子类的行为,又要为子类提供公共方法的时候选择抽象类。

从设计上来体会接口的意义

这里咱们从我个人比较熟悉的java入手,在java的设计中,经常出现的层的概念和模块的概念,个人经常做java Web的程序,我们以此为例,最经典的MVC结构,抽象一点理解,也就是控制、逻辑、数据三层,它们之间全部通过接口来通信。

在每一层里,又包含很多模块,每个模块对外则是一个整体,所以一个模块应该对外提供接口,其他地方需要某个功能时,可根据接口直接调用模块,也就是上面的 “ 接口是被其隔离部分的外观”。

设计中经常会提到组件,模块,其实不论是组件还是模块,都可以理解为 封装了一定功能的集合体, 一个类,一个功能块,一个插件,一个系统,都可以理解成组件、模块,因为,一个类可能是一个功能块的一部分,一个功能块可能是一个插件的一部分,一个插件可能是一个系统的一部分,小系统放到大系统中,也就是个组件罢了,就是组合的关系,从设计的角度,系统,子系统,插件、模块、组件等,其实说的就是一个东西,就是完成一定功能的封装体。

简单工厂

前面咧咧了那么多,看官们肯定看烦了,差评差评!这里我上面说了那么多接口的东西,总得用来看看吧,我们用一个例子来切入主题,这里我打算写一个功能,就是比对两个字符串相似的程度,肯定会有人说了,你这真废话,直接 equals() 它不香么!香是香,可是它不能展示(让我装13啊)呀,我们以java的方式来搞个相似度计算。

上面说了接口,那我们先定义接口:

public interface MatcherAlg {

    /**
     * 计算两个串的相似度
     * @param srcStr
     * @param dstStr
     * @return Float 相似值
     */
   public Float CalculateSimilarityRatioValue(String srcStr,String dstStr);

}

接口已经约束了我们这个功能只有一个方法,那么我们来内部实现一下:

public class JaccardMatcher implements MatcherAlg {

    @Override
    public Float CalculateSimilarityRatioValue(String srcStr, String dstStr) {
        if(srcStr == null && dstStr ==null){
            return 1f;
        }
        if(srcStr == null || dstStr == null){
            return 0f;
        }
        Set<Integer> aChar = srcStr.chars().boxed().collect(Collectors.toSet());
        Set<Integer> bChar = dstStr.chars().boxed().collect(Collectors.toSet());

        int intersection = SetUtils.intersection(aChar,bChar).size();
        if(intersection == 0){
            return 0f;
        }
        int union =  SetUtils.union(aChar,bChar).size();
        return ((float)intersection/(float)union);
    }
}

这时候我们要用它了,来比较两个字符串相似程度:

public class Test{

   public static void main(String args[]){
        String str= "sdfsf";
        String dst= "1234d";
        MatcherAlg matcher = new JaccardMatcher();
        Float result = matcher.CalculateSimilarityRatioValue(str,dst);
    }

}

运行一下,也十分正常,完美落地,可是仔细看下来,这样我定义那个MatcherAlg 接口,后面又

MatcherAlg matcher = new JaccardMatcher();

好像是在 “脱了裤子放p”,没事找事。干嘛不直接定义JaccardMatcher类,然后:

JaccardMatcher matcher = new JaccardMatcher();

但是上面说过了,我们应该面向接口编程,接口的核心就为了 “封装隔离”,实现类JaccardMatcher应该是被接口 MatcherAlg封装并同客户端隔离开来。

客户端根本不应该知道JaccardMatcher的存在,更不用说 newJaccardMatcher()这种“脱裤放p”操作了。但是问题又来了,如果客户端没有newJaccardMatcher(),只有MatcherAlg接口的定义,那么后面的代码是无法使用的。

于是纠结的地方出现了,上面花了那么大篇幅说怎么怎么面向接口,纯面向接口了你又不能运行了,能运行又违反了“隔离封装”了, 问题进入死环了。

所以“脱裤放p”的操作是对应这个死环一种蹩脚的写法(它可以运行,但专业的我们不认)。

这个死环如何解决,我们先看一下设计模式中的一段话,它是这样说的 :提供一个创建对象实例的功能,而无需关系其具体的实现。被创建实例的类型可以是接口、抽象类、也可以是具体的类。

受到那句话的启发,我们尝试得出一个解开上面那个死环的方案:我们在模块内部建一个类,这个类的功能就是创建可使用的接口,并且把创建的接口提供给客户端,这一客户只需要根据这个类来获取相应的接口对象,于此同时,接口具体使用哪个实现,我们就可以抽离到这个类里面,给我们提供了一个控制 使用哪个类的 隔离扩展区,客户端也不需要关心他用的这个类是对应哪种实现,如何实现的。

上面这套思想,设计模式中称之为 “工厂”

简单工厂的模式结构

样例代码:

//客户端类
public class Client {
    public static void main(String[] args) {
        Product p = SimpleFactory.makeProduct(Const.PRODUCT_A);
        p.show();
    }
}
//抽象产品
    public interface Product {
        void show();
    }
    //具体产品:ProductA
    public class ConcreteProduct1 implements Product {
        public void show() {
            System.out.println("具体产品1显示...");
        }
    }
    //具体产品:ProductB
    public class ConcreteProduct2 implements Product {
        public void show() {
            System.out.println("具体产品2显示...");
        }
    }
 //枚举
   public final class Const {
        static final int PRODUCT_A = 0;
        static final int PRODUCT_B = 1;
        static final int PRODUCT_C = 2;
    }
  //工厂
    public class SimpleFactory {
        public static Product makeProduct(int kind) {
            switch (kind) {
                case Const.PRODUCT_A:
                    return new ConcreteProduct1();
                case Const.PRODUCT_B:
                    return new ConcreteProduct2();
            }
            return null;
        }
    }

对简单工厂的理解

简单工厂的意义

首先看上面简单工厂的样例代码,有人会困惑,不就是把new操作从客户端移动到了额外的类里去了么,本质还是new 了一个实现类,这里我们再次回到原点,我们前面提到的接口,接口是用来封装隔离的,目的就是让客户端不要知道封装体内的具体实现,简单工厂的位置是处于封装体内的,简单工厂跟接口的具体实现在一起,算是封装体内部的一个类,所以简单工厂知道具体的实现类是没有关系的,我们再来看一下简单工厂的类图:

图中浅蓝色的虚线框即为一个封装的边界,表示接口、工厂、实现类组合成了一个组件,在这个组件中,只有接口和工厂是对外的,也只有这俩,外界可以使用和访问到,但是具体的实现类,完全是内部的,对外透明的,不可见的,所以它被全包裹进蓝框,对于客户端而言,它只知道这个Alg接口和生产含有Alg功能实例的工厂,通过Factory就能获取Alg的能力了,所以,new操作划在工厂内,在设计和隔离的意义上,有了质的变化。

简单工厂的别称

静态工厂

所谓静态工厂,就是我们使用工厂的时候,不需要实例化工厂了,直接将生产的方法设为静态方法,通过类名即可调用,或者做成单例的模式,也就是说简单工厂的方法通常都是静态的,所以称之为静态工厂。

万能工厂

一个简单工厂可以包含很多用来构建东西的方法,这些方法可以创建不同的接口、实力类,一个简单的工厂理论上可以构造任何东西,所以又称之为“万能工厂”

简单工厂的本质

简单工厂的本质是:选择实现

选择实现,重点在于选择,实现是已经做好了的,就算实现再简单(哪怕是new实例)也要由具体的实现类来实现,而不是在简单工厂里面来实现,简单工厂的目的在为客户端提供一个选择,选择哪种实现,从而使客户端和具体的实现之间解耦。这样具体实现无论如何变动,都不需要客户端随之变动,这个变动会在工厂这一层里被吸收和隔断。

实现简单工厂的难点在于“选择”的实现,可以通过传参,也可以通过动态的参数,比如在运行期间去读取配置文件或数据库、内存中的某个值,根据这个值来进行具体的实现。

扩展简单工厂:提供可配置的简单工厂

基本的实现套路,已经有较为明确的模板了,现在有一个问题,就是如果MatcherAlg的实现类不止一个,我们可以通过在工厂的方法中传入参数来处理

public Class Factory{

  public static MatcherAlg createAlg(String type){

        if( type.equals("a") ){
            return new aAlg();
        }else if ( type.equals("b") ){
            return new bAlg();
        }else{
               ……
        }

}
}

可是,当我们又又扩展了新的实现类的时候,if else 又需要扩展一句,同时对客户端也要告知,这样对于Factory这个类来说,严重违反了开闭原则。

为了解决这个问题,我们可以通过配置文件的形式来解决,当有了新的实现类或者需要默认指定用哪一个实现的时候,只需要通过配置文件的配置项即可,通过配置文件的方式,多需要使用java的反射来支持动态建立对象。这里摘取自己的一个代码来作为一个样例:

/**
 * 基础工厂,其他组件工厂的实现可用基于该类进行扩展
 * 功能:根据配置文件动态生成对象
 * @author GCC
 */
public abstract class AbstractFactory {

    private static Logger logger = Logger.getLogger(AbstractFactory.class);

    //默认自带的类控制配置文件
    private final static String DEFAULTCONFIG_FILE_URL = "factoryconfig.ini";

    //默认的配置文件
    static URL defaultConfigFileUrl = AbstractFactory.class.getClassLoader().getResource(DEFAULTCONFIG_FILE_URL);

    /**
     * 根据配置文件以及key值,获取对象的类路径
     * @param url   配置文件路径
     * @param key   关键字
     * @return  String 类路径
     */
    static String getClassUrl(String url,String key){
        ConfigUtil config = new ConfigUtil(url);
        return config.getValueByConfigkey(key);
    }

    /**
     * 根据指定配置文件及指定关键字生成对象
     * @param url   配置文件路径
     * @param key   关键字
     * @return  Object 具体对象
     */
    static Object getObject(String url,String key){
        String classurl = getClassUrl(url,key);
        try{
            Class oneclass = Class.forName(classurl);
            return oneclass.newInstance();
        }catch (Exception e){
            logger.error(e.getMessage() +" plase check"+ DEFAULTCONFIG_FILE_URL );
        }
        return null;
    }

}

配置文件(.ini文件)内容:

#matcher.algclassurl:算法类地址
matcher.algclassurl=org.gds.matcher.impl.LevenshteinMacther

简单工厂的缺陷

简单工厂实现简单,非常友好的提供了一套实现组件封装的功能,同时也解决了客户端何内部实现类的强耦合,实现了解耦。这是简单工厂的优点,但世事都是两面的,它也有不可避免地缺点:

首先,它增加了客户端的复杂程度,如果通过客户端的参数来选择具体的实现类,那客户端必须额外需要一份枚举表或者字典,并且知道每个枚举的意义,这样会增加客户端的复杂程度,同时一定程度上暴露了内部的实现(虽然可配置方案一定程度上可以对冲这一问题)。

其次,简单工厂使用静态方法(又叫静态工厂)来创建接口,当面临一些复杂的组件创建,静态方法会非常庞大,无法通过继承来扩展创建接口的方法的行为了。

以上就是如何从Java接口的角度切入静态工厂模式的详细内容,更多关于从Java接口的角度切入静态工厂模式的资料请关注我们其它相关文章!

(0)

相关推荐

  • java 静态工厂代替多参构造器的适用情况与优劣

    背景 假如现在你要想一个汉堡,有一个汉堡类:Hamburg.那么一般情况下你会: Hamburg hamburg = new Hamburg(); 情景一:不同参数数目的构造器 制作汉堡可以选择自定义,加肉,加菜,或者不添加,直接默认配方即可,那么会有以下几个构造器: Hamburg(); Hamburg(Meat meat); Hamburg(Meat meat,Vegetable vegetable); 当你要制作汉堡的时候,看到这么多的构造器,但是却不知道他们是什么意思,返回的汉堡到底有什

  • Java设计模式之静态工厂模式详解

    本文实例讲述了Java设计模式之静态工厂模式.分享给大家供大家参考,具体如下: 静态工厂模式(static factory)也叫简单工厂模式. 涉及到3个角色:工厂类角色,抽象产品类角色和具体产品类角色. 抽象产品类可以使用接口或者父类来描述产品对象的行为特征. 具体产品类就是某一具体的对象. 静态工厂类有一个静态的方法,含有判断逻辑,决定要创建哪一种具体的产品对象. 其设计模式如下: 抽象产品类  IProduct package org.test.design.sf; public inte

  • Java设计模式之单例模式简介

    一.饿汉式(静态常量) public class Face { private static final Face instance = new Face(); static Face getInstance() { return instance; } private Face() { } } 优点:这种写法比较简单,就是在类装载的时候就完成实例化.避免了线程同步问题. 缺点:在类装载的时候就完成实例化,没有懒加载的效果.如果从始至终从未使用过这个实例,则会造成内存的浪费. 二.饿汉式(静态代

  • Java通俗易懂系列设计模式之观察者模式

    介绍 观察者模式是行为设计模式之一.当您对对象的状态感兴趣并希望在有任何更改时收到通知时,观察者设计模式非常有用.在观察者模式中,监视另一个对象状态的对象称为Observer,正在被监视的对象称为Subject. 根据GoF,观察者设计模式的意图是; 定义对象之间的一对多依赖关系,以便当一个对象更改状态时,将自动通知和更新其所有依赖项. Subject包含一个观察者列表,用于通知其状态的任何变化,因此它应该提供观察者可以注册和注销自己的方法.Subject还包含一种方法,用于通知所有观察者任何更

  • java用静态工厂代替构造函数使用方法和优缺点

    1. 形式 复制代码 代码如下: public static Boolean valueOf(boolean b) {    return b ? Boolean.TRUE : Boolean.FALSE;} 2. 优点: 可以有名称不一定要创建新对象,可以返回已有的对象可以返回子类类型的对象(例:java.util.Collections)让参数化代码变短(例:new HashMap<String,List<String>>() 改为 HashMap.newInstance()

  • Java静态工厂方法的实例详解

     Java静态工厂方法的实例详解 什么是静态工厂方法 对于类而言,为了让使用者获取它自身的一个实例,最常用的方法就是提供一个公有的构造器. 当然,这里要介绍的是另一种方法--静态工厂方法,一个返回类的实例的静态方法. 举个例子,Boolean的一个将基本类型boolean转为封装类的方法,valueOf: public static Boolean valueOf(boolean b) { return (b ? TRUE : FALSE); } 为什么要使用静态工厂方法 那么,我们为什么要使用

  • 详解java设计模式之六大原则

    一.单一职责原则 1.单一职责定义 单一职责原则:一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因. 单一职责原则告诉我们:一个类不能太"累"!在软件系统中,一个类承担的职责越多,它被复用的可能性就越小,而且一个类承担的职责过多,就相当于将这些职责耦合在一起,当其中一个职责 变化时,可能会影响其他职责的运作,因此要将这些职责进行分离,将不同的职责封装在不同的类中,即将不同的变化原因封装在不同的类中,如果多个职责总是同时发生改变则可将它们封

  • 如何从Java接口的角度切入静态工厂模式

    面向接口编程 接口的定义及功能 这里从java介入吧,在java中,接口是一种特殊的类,接口里面的量都是常量,接口的方法只有定义而没有实现,换句话说,接口就像一个菜单,它只会告知你我有什么菜,而并不会有实际的菜品,所以通常用接口来定义实现类的外观,根据外部应用所需要的功能,约定实现类的能力(类的功能不仅限于接口约束).通过接口,可以实现不相关类的相同功能,而不考虑这些类之间的层次关系,接口就是实现类对外的外观. 上面那样说,可能显得很装13,那千言万语化成一句人话就是:1.定义功能,对外暴露 2

  • Java实现多线程中的静态代理模式

    前言 代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能. 简言之,代理模式就是设置一个中间代理来控制访问原目标对象,以达到增强原对象的功能和简化访问方式. 静态代理属于设计模式中的代理模式.反之则有动态代理,本篇文章不展开讲,有兴趣的可自行谷歌研究研究. 其实继承Thread也属于静态代理的一种,所以在这里学习静态代理有助于我们学习多线程. 静态代理 优点:可以在不修改目标对象的前提下扩

  • Java十分钟精通进阶简单工厂模式

    目录 1.什么是工厂模式? 工厂模式的定义: 2.简单工厂模式优点和缺点 3.简单工厂的代码实现 4.小结 1.什么是工厂模式? 工厂模式的定义: 定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中.这满足创建型模式中所要求的"创建与使用相分离"的特点. 现实生活中,原始社会自给自足(没有工厂),农耕社会小作坊(简单工厂,民间酒坊),工业革命流水线(工厂方法,自产自销),现代产业链代工厂(抽象工厂,富士康.我们可以看到工厂的变化趋势,但是对于我们来说,看到是

  • Java创建型设计模式之抽象工厂模式(Abstract Factory)

    目录 抽象工厂模式 概述 产品等级结构与产品族 优缺点 主要角色 抽象工厂模式的基本使用 创建抽象产品 创建具体产品 创建抽象工厂 创建具体工厂 客户端执行 抽象工厂模式 概述 抽象工厂模式(Abastract Factory Pattern)属于创建型模式,它提供了一种创建对象的最佳方式. 它提供一个创建一系列相关或相互依赖对象的接口,无须显式指定他们具体的类.每个生成的工厂都能按照工厂模式提供对象. 抽象工厂模式是围绕一个超级工厂创建其他工厂,该超级工厂又称为其他工厂的工厂. 产品等级结构与

  • 实例解析Java单例模式编程中对抽象工厂模式的运用

    定义:为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类. 类型:创建类模式 类图: 抽象工厂模式与工厂方法模式的区别         抽象工厂模式是工厂方法模式的升级版本,他用来创建一组相关或者相互依赖的对象.他与工厂方法模式的区别就在于,工厂方法模式针对的是一个产品等级结构:而抽象工厂模式则是针对的多个产品等级结构.在编程中,通常一个产品结构,表现为一个接口或者抽象类,也就是说,工厂方法模式提供的所有产品都是衍生自同一个接口或抽象类,而抽象工厂模式所提供的产品则是衍生自不同

  • Java中常用的设计模式之工厂模式详解

    目录 优点 缺点 使用场景 一.实现方式 1.定义一个接口 2.定义两个接口实现类 3.定义一个工厂类 二.测试 总结 优点 1.一个调用者想创建一个对象,只要知道其名称就可以了. 2.扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以. 3.屏蔽产品的具体实现,调用者只关心产品的接口. 缺点 1.每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖.这并不是什么好事. 使用场景 1.日志记录器:记录

  • 精通Java接口的使用与原理

    接口基础知识 为什么需要接口 需求:要求实现防盗门的功能,门有“开”和“关”的功能,锁有“上锁”和“开锁”功能.分析:首先防盗门是一个门,它有一把锁.按照之前学过的面向对象的思想,可以将门和锁分别定义成抽象类.但是,不能让防盗门在继承们的同时又继承锁.原因有两点:第一,防盗门不是锁,不符合继承中is a的关系,第二,Java只支持单继承,如何解决这个问题呢?这时就要用到接口,可以将锁定义为接口,让防盗门继承门,实现锁的接口.那么什么是借口呢?它的特点又是什么呢?下面让我们来学习一下. 什么是接口

  • java设计模式之工厂模式实例详解

    本文实例讲述了java设计模式之工厂模式.分享给大家供大家参考,具体如下: 工厂模式(factory) 涉及到4个角色:抽象工厂类角色,具体工厂类角色,抽象产品类角色和具体产品类角色. 抽象工厂类角色使用接口或者父类来描述工厂的行为, 具体工厂类角色负责创建某一类型的产品对象. 抽象产品类可以使用接口或者父类来描述产品对象的行为特征. 具体产品类就是某一具体的对象. 工厂模式不同于静态工厂模式的地方: 工厂模式在工厂类也实现了多态,而不仅仅是在产品对象上实现多态. 它可以应对不同类型的产品对应一

  • 23种设计模式(2) java工厂模式

    23种设计模式第二篇:java工厂模式 定义: 工厂模式是 Java 中最常用的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的. 工厂模式根据抽象程度的不同分为三种: 简单工厂模式(也叫静态工厂模式) 工厂方法模式(也叫多形性工厂) 抽象工厂模式(也叫工具箱) 简单工厂模式 实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接

随机推荐