geotools

geotools学习(八)工厂模式

2019-10-29  本文已影响0人  MrSwilder

工厂模式

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

回顾

每个插件jar有:

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将在系统属性中查找键。

  1. 然后FactoryRegistry将搜索元inf /services中的键的资源路径。
  1. 这意味着FactoryRegistry将能够找到类路径中提供的任何FunctionFactory。

定义自己的FactoryFinder

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

  1. 以惰性的方式创建FactoryRegistry,列出您感兴趣的接口(称为类别)。
  2. GeoTools通常在“Finder”类中保存一个FactoryRegistry:
  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的提示:

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注册
上一篇下一篇

猜你喜欢

热点阅读