美文网首页geotools
geotools学习(八)工厂模式

geotools学习(八)工厂模式

作者: MrSwilder | 来源:发表于2019-10-29 09:45 被阅读0次

    工厂模式

    我们将使用FunctionFactory作为GeoTools插件系统的介绍。
    在体系结构中,我们看到了核心GeoTools库和提供功能的插件之间的区别。使插件工作的一个因素是“工厂SPI”插件系统(这实际上是Java的一部分,而不是我们编造的)。
    参考:

    回顾

    每个插件jar有:

    • meta - inf / services文件夹
      该文件夹包含文件列表(每个接口名一个)
    • 这些文件包含实现该接口的类的列表
      这一页是我们解释这一切如何运作的地方。

    GeoTools使用Factory和FactoryRegistry类进行扩展。标准的工厂模式为我们提供了关于将要发生什么的线索:

    工厂模式工厂是创建其他对象的对象。

    这就是乐趣开始的地方……

    步骤1的接口

    第一步是定义你的接口;在本例中,我们将使用GeoTools已经提供的函数接口。


    image.png

    GeoTools喜欢使用接口,这就是为什么(这是创建基于插件的库的第一步)。

    我们将以函数为例进行讨论:

    public interface Function extends Expression {
        String getName();
        List<Expression> getParameters();
        Literal getFallbackValue();
    }
    public interface Expression {
        Object evaluate(Object object);
        <T> T evaluate(Object object, Class<T> context);
        Object accept(ExpressionVisitor visitor, Object extraData);
    }
    

    1.下面是一个快速实现从一个点到一行:

    /*
     *    GeoTools - The Open Source Java GIS Toolkit
     *    http://geotools.org
     *
     *    (C) 2019, Open Source Geospatial Foundation (OSGeo)
     *
     *    This library is free software; you can redistribute it and/or
     *    modify it under the terms of the GNU Lesser General Public
     *    License as published by the Free Software Foundation;
     *    version 2.1 of the License.
     *
     *    This library is distributed in the hope that it will be useful,
     *    but WITHOUT ANY WARRANTY; without even the implied warranty of
     *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     *    Lesser General Public License for more details.
     *
     */
    
    package org.geotools.tutorial.function;
    
    import java.util.List;
    import org.geotools.filter.capability.FunctionNameImpl;
    import org.geotools.util.Converters;
    import org.locationtech.jts.geom.Coordinate;
    import org.locationtech.jts.geom.Geometry;
    import org.locationtech.jts.geom.Point;
    import org.locationtech.jts.linearref.LinearLocation;
    import org.locationtech.jts.linearref.LocationIndexedLine;
    import org.opengis.filter.capability.FunctionName;
    import org.opengis.filter.expression.Expression;
    import org.opengis.filter.expression.ExpressionVisitor;
    import org.opengis.filter.expression.Function;
    import org.opengis.filter.expression.Literal;
    
    /**
     * Quick function that illustrates snapping to a line.
     *
     * @author jody
     */
    public class SnapFunction implements Function {
    
        static FunctionName NAME =
                new FunctionNameImpl(
                        "snap",
                        Point.class,
                        FunctionNameImpl.parameter("point", Point.class),
                        FunctionNameImpl.parameter("line", Geometry.class));
    
        private final List<Expression> parameters;
    
        private final Literal fallback;
    
        public SnapFunction(List<Expression> parameters, Literal fallback) {
            if (parameters == null) {
                throw new NullPointerException("parameters required");
            }
            if (parameters.size() != 2) {
                throw new IllegalArgumentException("snap( point, line) requires two parameters only");
            }
            this.parameters = parameters;
            this.fallback = fallback;
        }
    
        public Object evaluate(Object object) {
            return evaluate(object, Point.class);
        }
    
        public <T> T evaluate(Object object, Class<T> context) {
            Expression pointExpression = parameters.get(0);
            Point point = pointExpression.evaluate(object, Point.class);
    
            Expression lineExpression = parameters.get(1);
            Geometry line = lineExpression.evaluate(object, Geometry.class);
    
            LocationIndexedLine index = new LocationIndexedLine(line);
    
            LinearLocation location = index.project(point.getCoordinate());
    
            Coordinate snap = index.extractPoint(location);
    
            Point pt = point.getFactory().createPoint(snap);
    
            return Converters.convert(pt, context); // convert to requested format
        }
    
        public Object accept(ExpressionVisitor visitor, Object extraData) {
            return visitor.visit(this, extraData);
        }
    
        public String getName() {
            return NAME.getName();
        }
    
        public FunctionName getFunctionName() {
            return NAME;
        }
    
        public List<Expression> getParameters() {
            return parameters;
        }
    
        public Literal getFallbackValue() {
            return fallback;
        }
    }
    

    2.如果您感兴趣,可以在“从点到线”单元中了解使用LocationIndexLine的机制。

    1. 需要注意的一点是,FunctionName的定义用于向新函数的用户描述有效的参数。
      按照惯例,我们将其定义为一个静态的final SnapFunction。但是,这只是一个约定(它将有助于实现下一节)。

    步骤2工厂

    接口不允许有构造函数,因此任何构造函数都被定义为工厂接口。
    继续我们的例子:

    public interface FunctionFactory {
        List<FunctionName> getFunctionNames();
        Function function(String name, List<Expression> args, Literal fallback);
    }
    

    上面的工厂描述了哪些函数可用,并允许创建函数。到目前为止一切正常。以上就是普通的“工厂模式”通常的工作方式—希望您熟悉它?
    为了继续我们的实现,我们将定义ExampleFunctionFactory:
    1.创建实现FunctionFactory的ExampleFunctionFactory
    2.填写以下资料:

    /*
     *    GeoTools - The Open Source Java GIS Toolkit
     *    http://geotools.org
     *
     *    (C) 2019, Open Source Geospatial Foundation (OSGeo)
     *
     *    This library is free software; you can redistribute it and/or
     *    modify it under the terms of the GNU Lesser General Public
     *    License as published by the Free Software Foundation;
     *    version 2.1 of the License.
     *
     *    This library is distributed in the hope that it will be useful,
     *    but WITHOUT ANY WARRANTY; without even the implied warranty of
     *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     *    Lesser General Public License for more details.
     *
     */
    
    package org.geotools.tutorial.function;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import org.geotools.feature.NameImpl;
    import org.geotools.filter.FunctionFactory;
    import org.opengis.feature.type.Name;
    import org.opengis.filter.capability.FunctionName;
    import org.opengis.filter.expression.Expression;
    import org.opengis.filter.expression.Function;
    import org.opengis.filter.expression.Literal;
    
    public class ExampleFunctionFactory implements FunctionFactory {
    
        public List<FunctionName> getFunctionNames() {
            List<FunctionName> functionList = new ArrayList<>();
            functionList.add(SnapFunction.NAME);
            return Collections.unmodifiableList(functionList);
        }
    
        public Function function(String name, List<Expression> args, Literal fallback) {
            return function(new NameImpl(name), args, fallback);
        }
    
        public Function function(Name name, List<Expression> args, Literal fallback) {
            if (SnapFunction.NAME.getFunctionName().equals(name)) {
                return new SnapFunction(args, fallback);
            }
            return null; // we do not implement that function
        }
    }
    

    3.我们引用静态的final SnapFunction.NAME。
    虽然我们提到这只是一个约定,但是您可以自由地创建一个新的FunctionNameImpl(“snap”、“point”、“line”)作为getFunctionNames()方法的一部分。这样做的好处是避免在用户按名称请求SnapFunction之前加载它。
    4.我们现在可以注册我们的工厂了。
    创建文件:META_INF/services/org.geotools.filter.FunctionFactory
    5.填写以下内容(每行一个实现类):

    org.geotools.tutorial.function.ExampleFunctionFactory
    

    6.那就是SnapFunction现在发布了!

    步骤3 FactoryRegistry

    GeoTools 2.2使用javax.imageio.ServiceRegistry魔力(此插件系统起源的地方)。请注意,FactoryRegistry将缓存已经找到的工厂。由于工厂是无状态的,这应该不是问题。

    直接使用FactoryRegistry

    1.你可以直接使用FactoryRegistry在你自己的代码:

    Set categories = Collections.singleton(new Class[] {FunctionFactory.class,});
    FactoryRegistry registry = new FactoryRegistry(categories);
    
    Iterator iterator = registry.getProviders(FunctionFactory.class);
    

    2.在内部,FactoryRegistry将在系统属性中查找键。

    • 如果密钥不存在或抛出SecurityException,则失败。
    • 否则,尝试实例化给定的类。
    1. 然后FactoryRegistry将搜索元inf /services中的键的资源路径。
    • 如果找到资源,就读取文件并实例化类。
    • 如果资源不存在,则失败。
    1. 这意味着FactoryRegistry将能够找到类路径中提供的任何FunctionFactory。

    定义自己的FactoryFinder

    注意,FactoryRegistry不是同步的,为了保护这一点,您可以在FactoryFinder中包装直接使用,它还提供了类型安全性。
    下面是使用FactoryRegistry作为FactoryFinder的一部分:

    1. 以惰性的方式创建FactoryRegistry,列出您感兴趣的接口(称为类别)。
    2. GeoTools通常在“Finder”类中保存一个FactoryRegistry:
    • 创建ExampleFinder
    1. 填写以下资料:
    /*
     *    GeoTools - The Open Source Java GIS Toolkit
     *    http://geotools.org
     *
     *    (C) 2019, Open Source Geospatial Foundation (OSGeo)
     *
     *    This library is free software; you can redistribute it and/or
     *    modify it under the terms of the GNU Lesser General Public
     *    License as published by the Free Software Foundation;
     *    version 2.1 of the License.
     *
     *    This library is distributed in the hope that it will be useful,
     *    but WITHOUT ANY WARRANTY; without even the implied warranty of
     *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     *    Lesser General Public License for more details.
     *
     */
    
    package org.geotools.tutorial.function;
    
    import java.util.Set;
    import java.util.stream.Stream;
    import org.geotools.filter.FunctionFactory;
    import org.geotools.util.LazySet;
    import org.geotools.util.factory.FactoryCreator;
    import org.geotools.util.factory.FactoryFinder;
    import org.geotools.util.factory.FactoryRegistry;
    import org.geotools.util.factory.Hints;
    import org.opengis.filter.FilterFactory;
    
    public class ExampleFinder extends FactoryFinder {
    
        private static volatile FactoryCreator registry;
    
        private static FactoryRegistry getServiceRegistry() {
            assert Thread.holdsLock(ExampleFinder.class);
            if (registry == null) {
                Class<?> categories[] = new Class<?>[] {FunctionFactory.class};
                registry = new FactoryCreator(categories);
            }
            return registry;
        }
    
        /**
         * Returns a set of all available implementations for the {@link FilterFactory} interface.
         *
         * @param hints An optional map of hints, or {@code null} if none.
         * @return Set of available filter factory implementations.
         */
        public static synchronized Set<FunctionFactory> getFilterFactories(Hints hints) {
            hints = mergeSystemHints(hints);
            Stream<FunctionFactory> serviceProviders =
                    getServiceRegistry().getFactories(FunctionFactory.class, null, hints);
            return new LazySet<>(serviceProviders);
        }
    
        /** Allow the classpath to be rescanned */
        public static synchronized void scanForPlugins() {
            if (registry != null) {
                registry.scanForPlugins();
            }
        }
    }
    
    1. 以上只是一个例子,请使用FunctionFinder
      实现自己的FactoryFinder的提示:
    • 代码示例使用了LazySet,这使我们不必每次都检查类路径。
    • 实用程序方法addDefaultHints用于将全局地理工具配置应用于用户提供的提示。
    • 如上所示,您可以为客户端代码添加一些帮助方法。这通常用于根据某些条件执行搜索,或用于定位给定任务的“最佳”工厂。

    FactoryIteratorProviders

    factoryiteratorprovider用于支持其他插件机制。

    默认情况下,“工厂SPI”机制用于定位FactoryFinder(和FactoryRegistry)提供的工厂。但是为了支持其他插件机制,工厂有一个addFactoryIteratorProvider(…)方法。该方法允许开发人员添加一个迭代器,该迭代器知道如何处理另一个扩展机制。例如,在Eclipse中,可以添加一个FactoryIteratorProvider,它返回一个知道如何处理Eclipse扩展点并可以从Eclipse扩展创建工厂的提供者

    Abstract

    现在我们已经帮助客户端代码使用了我们的接口,下一步是提供一个抽象类来帮助开发实现。
    大多数GeoTools工厂都提供了一个抽象的超类来开始您的实现工作。当你制造自己的工厂时,这是一个很好的例子。

    1. 下面是一个AbstractFunction示例,以了解它所涉及的内容。
      这不是GeoTools的一部分(还没有)——它只是显示了使用的方法:
    /*
     *    GeoTools - The Open Source Java GIS Toolkit
     *    http://geotools.org
     *
     *    (C) 2019, Open Source Geospatial Foundation (OSGeo)
     *
     *    This library is free software; you can redistribute it and/or
     *    modify it under the terms of the GNU Lesser General Public
     *    License as published by the Free Software Foundation;
     *    version 2.1 of the License.
     *
     *    This library is distributed in the hope that it will be useful,
     *    but WITHOUT ANY WARRANTY; without even the implied warranty of
     *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     *    Lesser General Public License for more details.
     *
     */
    
    package org.geotools.tutorial.function;
    
    import java.util.Collections;
    import java.util.List;
    import org.geotools.util.Converters;
    import org.opengis.filter.capability.FunctionName;
    import org.opengis.filter.expression.Expression;
    import org.opengis.filter.expression.ExpressionVisitor;
    import org.opengis.filter.expression.Function;
    import org.opengis.filter.expression.Literal;
    
    public abstract class AbstractFunction implements Function {
        protected final FunctionName name;
        protected final List<Expression> params;
        protected final Literal fallback;
    
        protected AbstractFunction(FunctionName name, List<Expression> args, Literal fallback) {
            this.name = name;
            this.params = args;
            this.fallback = fallback;
        }
    
        public abstract Object evaluate(Object object);
    
        public <T> T evaluate(Object object, Class<T> context) {
            Object value = evaluate(object);
            return Converters.convert(value, context);
        }
    
        public Object accept(ExpressionVisitor visitor, Object extraData) {
            return visitor.visit(this, extraData);
        }
    
        public String getName() {
            return name.getName();
        }
    
        public FunctionName getFunctionName() {
            return name;
        }
    
        public List<Expression> getParameters() {
            return Collections.unmodifiableList(params);
        }
    
        public Literal getFallbackValue() {
            return fallback;
        }
        // helper methods
        <T> T eval(Object feature, int index, Class<T> type) {
            Expression expr = params.get(index);
            Object value = expr.evaluate(feature, type);
            return type.cast(value);
        }
    }
    
    1. 以下是使用示例。
      注意,我们已经减少了开发人员需要填写的方法的数量,并且我们提供了一个助手方法来避免一些与参数评估相关的“锅炉板”剪切和粘贴代码:
    /*
     *    GeoTools - The Open Source Java GIS Toolkit
     *    http://geotools.org
     *
     *    (C) 2019, Open Source Geospatial Foundation (OSGeo)
     *
     *    This library is free software; you can redistribute it and/or
     *    modify it under the terms of the GNU Lesser General Public
     *    License as published by the Free Software Foundation;
     *    version 2.1 of the License.
     *
     *    This library is distributed in the hope that it will be useful,
     *    but WITHOUT ANY WARRANTY; without even the implied warranty of
     *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     *    Lesser General Public License for more details.
     *
     */
    
    package org.geotools.tutorial.function;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import org.geotools.feature.NameImpl;
    import org.geotools.filter.FunctionFactory;
    import org.geotools.filter.capability.FunctionNameImpl;
    import org.locationtech.jts.geom.Coordinate;
    import org.locationtech.jts.geom.Geometry;
    import org.opengis.feature.type.Name;
    import org.opengis.filter.capability.FunctionName;
    import org.opengis.filter.expression.Expression;
    import org.opengis.filter.expression.Function;
    import org.opengis.filter.expression.Literal;
    
    public class ExampleFunctionFactory2 implements FunctionFactory {
        private ArrayList<FunctionName> functionList;
        private static FunctionName FIRST = new FunctionNameImpl("first", "geometry");
    
        public synchronized List<FunctionName> getFunctionNames() {
            if (functionList == null) {
                functionList = new ArrayList<>();
                functionList.add(FIRST);
            }
            return Collections.unmodifiableList(functionList);
        }
    
        public Function function(String name, List<Expression> args, Literal fallback) {
            return function(new NameImpl(name), args, fallback);
        }
    
        public Function function(Name name, List<Expression> args, Literal fallback) {
            if (new NameImpl("first").equals(name)) {
                return new AbstractFunction(FIRST, args, fallback) {
                    public Geometry evaluate(Object object) {
                        Geometry geom = eval(object, 0, Geometry.class);
                        Coordinate coordinate = geom.getCoordinate();
                        return geom.getFactory().createPoint(coordinate);
                    }
                };
            }
            return null; // we do not implement that function
        }
    }
    
    1. 您可以看到这将如何帮助快速地获得一组函数。

    插件清单

    允许客户端贡献一个插件
    1. 定义一个接口
      Example: Foo
    2. 定义工厂接口
      Example: FooFactory
    3. 定义FactoryFinder
      Example: FooFactoryFinder
    4. 为实现者定义一个抽象类
      Example: AbstractFoo
    允许客户端代码访问插件

    1.将您的FactoryFinder公开
    Example: FooFinder

    在实现插件时
    1. 创建您的实现
      Example: MyFoo
    2. 创建您的扩展工厂
      Example: MyFooFactory
      Example: META-INF/services/Foo
    3. 在META-INF/services注册

    相关文章

      网友评论

        本文标题:geotools学习(八)工厂模式

        本文链接:https://www.haomeiwen.com/subject/amhwvctx.html