Обработка различных версий API в том же источнике Java

Ваш scaleBand().domain() должен быть массивом для Xscale. В моем решении я выбираю индексы значений в качестве массива. Вы можете сопоставить свои данные (обычно массив объектов) с другими значениями объектов в массиве. Кроме того, было несколько других проблем с масштабированием с точки зрения высоты и ширины фактических стержней и их расположения. Имейте в виду, что источник SVG - это верхний левый угол, и все относительно этого.

Я обновил приведенный ниже код, который вносит необходимые изменения для создания гистограммы. Пожалуйста, пройдите через это и дайте мне знать, если есть что-то, что вы не понимаете.

var data = [1, 2, 3, 4, 5];
var svg = d3.select("svg");
var margin = 100,
  width = svg.attr("width") - margin,
  height = svg.attr("height") - margin;
var Xscale = d3.scaleBand()
  .domain(data.map((e,i) => i)) //returns array [0,1,2,3,4] for the index of the values
  .range([0, width])
  .padding(0.2);
var dmax = d3.max(data) //calculates the max value of the data
var Yscale = d3.scaleLinear()
  .domain([0, dmax])
  .range([height, 0]);
  
var g = svg.append("g")
  .attr("transform", "translate(" + 50 + "," + 50 + ")");

var x = g.append("g")
  .attr("transform", "translate(0," + height + ")")
  .call(d3.axisBottom(Xscale).tickFormat(function(d) {
    return d;
  }).ticks(10))
 .append("text")
 .attr("x", 6)
 .attr("text-anchor", "end")
 .text("index");

var y = g.append("g")
  .call(d3.axisLeft(Yscale).tickFormat(function(d) {
    return d;
  }).ticks(10))
  .append("text")
  .attr("y", 6)
  .attr("dy", "0.71em")
  .attr("text-anchor", "end")
  .text("value");

g.selectAll(".bar")
  .data(data)
  .enter().append("rect")
  .attr("class", "bar")
  .attr("x", function(d, i){ return Xscale(i)}) //move the bar to the x position where it should appear
  .attr("y", function(d, i) { return Yscale(d); }) //move the bar from the top down to the level of the value.
  .attr("width", Xscale.bandwidth() ) //the width of the bar is the width between the points on the x-axis 
  .attr("height", function(d, i) {
    return Yscale(dmax-d);
  }); // the height of the points is calculated based on the scale and the difference between this point and the max value of the data.
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  <title>Bar chart with D3.js</title>
  <link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
  <script src="https://d3js.org/d3.v5.min.js"></script>

</head>

<body>
  <div id='layout'>
    <h2>Bar chart example</h2>
    <div class='container'>
      <svg class="chart" height="500" width="1000" />
    </div>
  </div>
  <p>Why this is not working?? </p>
</body>

</html>

6
задан Marcus Leon 6 April 2009 в 22:27
поделиться

8 ответов

Java действительно не был предназначен для этой условной компиляции (в отличие от C++), и это честно походит на рецепт для окончания в "аду пути к классу".

В то время как Вы могли вручную начать иметь дело с функциями, которые возвращают версию Вашего API, у Вас затем есть classfile, который соответствует определенной версии, но без признаков, что это может быть несовместимым.

Я встретился с этой ситуацией прежде (например, работающий с различными версиями Eclipse), и это не симпатично. То, что я закончил тем, что делал, имеет интерфейс с двумя различными реализациями, один для каждого API, поместило каждого из них в отдельном проекте (плагин в моем случае) и затем попыталось загрузить их фабрикой или инжекцией. Изолируйте их, а также Вы можете.

2
ответ дан 10 December 2019 в 02:53
поделиться

Можно попробовать

  • Основанный на отражении вызов или Генерация кода или старый метод предварительной обработки или

  • Стратегическая модель для инкапсуляции, что варьируется.

class ThirdPartyApi {
     void foo(){}  // available in all versions
     void bar(){}  // available only in new version
}

ThirdPartyApiV1 extends ThirdPartyApi {
     void foo() {
        thirdpartyV1Object.foo();
     }
}

ThirdPartyApiV2 extends ThirdPartyApi {
     void foo() {
        thirdpartyV2Object.foo();
     }
     void bar() {
        thirdpartyV2Object.bar();
     }
}

Используйте DependencyInjection для введения правильной версии реализации ThridPartyApi. Иначе используйте ThirdPartyApiFactory для создания соответствующего экземпляра на основе системного значения свойства или конфигурации.

-1
ответ дан 10 December 2019 в 02:53
поделиться

Вы могли также разделить ответвления своей системы управления версиями, которая содержит ориентированное на клиента (т.е. конкретная версия) код

1
ответ дан 10 December 2019 в 02:53
поделиться

Самый безопасный подход должен отступить к самой низкой версии, которую необходимо поддерживать. Это предполагает, что все версии назад совместимы, который не обязательно имеет место.

Если то решение не является соответствующим или желательным затем, что я отступаю к внедрению зависимости. Платформа Spring является безусловно самой популярной и общей платформой DI, но никакими средствами единственное. Guice - другой. Вы можете даже самокрутка, если это - нежелательный для добавления полной платформы для этого.

Но у меня есть проблемы при предположении JAVA-приложения - особенно приложения Web/J2EE - что я обхожусь без использования Spring. Это просто слишком полезно.

Скажем, существует 4 версии соответствующей банки. API изменился дважды в то время, таким образом, у Вас есть 3 различных версии API. Необходимо абстрагировать использование той банки к собственному API, который последователен через все эти версии, и затем создайте три реализации его: один для каждой различной версии API.

В Spring Вы создаете контекст приложения, который определяет все Ваши бобы и как они введены в другие бобы. Нет никакой причины, которую Вы не можете выбрать или создать и контекст приложения как часть процесса сборки. Часто свойства используются для этого, но Вы могли также включать часть контекста приложения это слишком.

Ключевой пункт здесь - то, что даже при том, что API отличаются, необходимо абстрагировать далеко те различия, что касается кода. Если Вы не делаете Вы просто напрашиваетесь на неприятности, и это просто становится более грязным.

5
ответ дан 10 December 2019 в 02:53
поделиться

У меня была эта та же потребность, так как у нас есть код, который должен работать на всех версиях Java от Java 1.2, но некоторый код должен использовать в своих интересах более новый API, если они доступны.

После различных перестановок с помощью отражения для получения объектов Метода и вызывая их динамично я обосновался на подходе стиля обертки настолько же лучше всего, в целом (хотя при некоторых обстоятельствах, просто храня отраженный Метод как помехи и вызывая это лучше - это зависит).

Следующее является примером "Системный класс" Утилиты, который выставляет определенный более новый API. Этот пример использует Singleton, но мог легко инстанцировать нескольких объектов, если базовому API было нужно это.

Существует два класса:

  • SysUtil
  • SysUtil_J5

Последний является тем, используемым, если JVM во время выполнения является Java 5 или позже. Иначе методы нейтрализации, которые совместимы в контракте, используются от реализации по умолчанию в SysUtil, который использует только Java 4 или более ранние API. Каждый класс компилируется с компилятором определенной версии, так, чтобы не было никакого случайного использования Java 5 + API в классе Java 4:

SysUtil (скомпилированный с компилятором Java 4)

import java.io.*;
import java.util.*;

/**
 * Masks direct use of select system methods to allow transparent use of facilities only
 * available in Java 5+ JVM.
 *
 * Threading Design : [ ] Single Threaded  [x] Threadsafe  [ ] Immutable  [ ] Isolated
 */

public class SysUtil
extends Object
{

/** Package protected to allow subclass SysUtil_J5 to invoke it. */
SysUtil() {
    super();
    }

// *****************************************************************************
// INSTANCE METHODS - SUBCLASS OVERRIDE REQUIRED
// *****************************************************************************

/** Package protected to allow subclass SysUtil_J5 to override it. */
int availableProcessors() {
    return 1;
    }

/** Package protected to allow subclass SysUtil_J5 to override it. */
long milliTime() {
    return System.currentTimeMillis();
    }

/** Package protected to allow subclass SysUtil_J5 to override it. */
long nanoTime() {
    return (System.currentTimeMillis()*1000000L);
    }

// *****************************************************************************
// STATIC PROPERTIES
// *****************************************************************************

static private final SysUtil            INSTANCE;
static {
    SysUtil                             instance=null;

    try                  { instance=(SysUtil)Class.forName("SysUtil_J5").newInstance(); } // can't use new SysUtil_J5() - compiler reports "class file has wrong version 49.0, should be 47.0"
    catch(Throwable thr) { instance=new SysUtil();                                                                    }
    INSTANCE=instance;
    }

// *****************************************************************************
// STATIC METHODS
// *****************************************************************************

/**
 * Returns the number of processors available to the Java virtual machine.
 * <p>
 * This value may change during a particular invocation of the virtual machine. Applications that are sensitive to the
 * number of available processors should therefore occasionally poll this property and adjust their resource usage
 * appropriately.
 */
static public int getAvailableProcessors() {
    return INSTANCE.availableProcessors();
    }

/**
 * Returns the current time in milliseconds.
 * <p>
 * Note that while the unit of time of the return value is a millisecond, the granularity of the value depends on the
 * underlying operating system and may be larger. For example, many operating systems measure time in units of tens of
 * milliseconds.
 * <p>
 * See the description of the class Date for a discussion of slight discrepancies that may arise between "computer time"
 * and coordinated universal time (UTC).
 * <p>
 * @return         The difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.
 */
static public long getMilliTime() {
    return INSTANCE.milliTime();
    }

/**
 * Returns the current value of the most precise available system timer, in nanoseconds.
 * <p>
 * This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock
 * time. The value returned represents nanoseconds since some fixed but arbitrary time (perhaps in the future, so values
 * may be negative). This method provides nanosecond precision, but not necessarily nanosecond accuracy. No guarantees
 * are made about how frequently values change. Differences in successive calls that span greater than approximately 292
 * years (263 nanoseconds) will not accurately compute elapsed time due to numerical overflow.
 * <p>
 * For example, to measure how long some code takes to execute:
 * <p><pre>
 *    long startTime = SysUtil.getNanoTime();
 *    // ... the code being measured ...
 *    long estimatedTime = SysUtil.getNanoTime() - startTime;
 * </pre>
 * <p>
 * @return          The current value of the system timer, in nanoseconds.
 */
static public long getNanoTime() {
    return INSTANCE.nanoTime();
    }

} // END PUBLIC CLASS

SysUtil_J5 (скомпилированный с компилятором Java 5)

import java.util.*;

class SysUtil_J5
extends SysUtil
{

private final Runtime                   runtime;

SysUtil_J5() {
    super();

    runtime=Runtime.getRuntime();
    }

// *****************************************************************************
// INSTANCE METHODS
// *****************************************************************************

int availableProcessors() {
    return runtime.availableProcessors();
    }

long milliTime() {
    return System.currentTimeMillis();
    }

long nanoTime() {
    return System.nanoTime();
    }

} // END PUBLIC CLASS
0
ответ дан 10 December 2019 в 02:53
поделиться

То, что я сделал в прошлом: Максимально чисто запишите минимальный объем кода, который взаимодействует с зависимыми аспектами версии библиотеки. Имейте версию этого кода для каждой версии библиотеки. Имейте их всех, реализуют тот же интерфейс. Объем Вашего приложения должен попробовать к (с Class.forName и возможно немного отражения для конструкции), динамично загружают версию, подходящую для последней библиотеки. Если это перестало работать, отступите к статически связанной версии для старой библиотеки.

Соответствующим использованием sourcepath и пути к классу, можно принять меры, чтобы базовый код препятствовался пользоваться новой библиотекой.

1
ответ дан 10 December 2019 в 02:53
поделиться

Можно использовать самоанализ Java. Посмотрите на пакет:

java.lang.reflect

Это имеет вызываемый метод класса. Можно получить все открытые методы использования Класса:

Method[] methodList = obj.getClass().getMethods();

Так как это - API, методы set были бы общедоступны. Затем можно пробежать массив methodList и проверить на те методы, которые имеют то же имя как методы set. При нахождении их используйте их. Иначе Вы знаете, что это - более ранняя версия.

Кроме того, большая часть хорошо разработанного API имеют функцию, которая возвращает значение текущей версии файла JAR.

Например:

String currentVersion = api.SomeClass.version() ;

Попытайтесь проверить, существует ли функция как этот в API, который Вы используете. Это было бы легче.

0
ответ дан 10 December 2019 в 02:53
поделиться

Можно скомпилировать в наименьший общий знаменатель, затем использовать отражение для вызова метода, который только доступен на более поздних API. Например, если на классе com.foo. Панель, метод "getFoogle ()" был заменен в более поздних версиях Вашего API методом "getFiggle ()". И давайте предположим, что метод (в любом варианте) берет интервал и двойное и возвращает интервал. Вы выполняете вызов обертки следующим образом:

public int getFoogleFiggle(Bar bar, int n, double d) {
  try {
    Class clz = Class.forName("com.foo.Bar");
    Method m = clz.getMethod("getFiggle", new Class[] {Integer.class, Double.class});
    return (Integer) m.invoke(bar, new Object[] {n, d});
  } catch (NoSuchMethodException nsme) {
    return getFoogle(n, d);
  } catch (various other spurious exceptions) {
    ... deal with in intesresting ways ...
  }
}

Обратите внимание, что во время компиляции, компилятор не заботится действительно ли класс coo.foo. Панель и/или метод getFiggle существуют.

1
ответ дан 10 December 2019 в 02:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: