2010-09-03 5 views
2

Скажем, у нас есть метод «общедоступный метод», который использует частный «subMethod1» и частный «subMethod2» в его вычислении. Эти subMethods не служат никакой другой цели, кроме как разбить «метод» на более читаемые инкапсулированные части и избежать дублирования кода внутри метода.Обмен примитивными данными между методами?

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

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

Ну, вы говорите, просто оберните примитивы в объекты, сохраните их локально внутри метода, затем попросите «метод» передать эти объекты в свои подтемы, где они смогут изменить свое значение, поскольку оно передано по ссылке как все объекты. Проблема в том, что «метод» используется внутри цикла моделирования, где он будет выполняться миллионы раз. Обертки объектов замедляют замедление, и здесь важна производительность.

Так что мой вопрос: есть ли какое-либо решение, которое даст мне такое же представление, как мои уродливые «использовать члены для совместного использования примитивных данных между методами», но также будет иметь хороший дизайн OO? Будет ли подходящим внутренний класс, обертывающий метод, под-методы и примитивные данные?

Благодаря, Ионы

EDIT: Вот это приготовленный образец кода, единственной целью которого является продемонстрировать замедление, вызванное использованием объектов обертки, как описано выше:

public class TestSharedMembers { 

protected int m17Multiples; 
protected int m23Multiples; 

public int methodPrimitive(int i) { 

    m17Multiples = 0; 
    m23Multiples = 0; 

    calc17Primitive(i); 
    calc23Primitive(i); 

    return m17Multiples + m23Multiples; 
} 

private void calc17Primitive(int i) { 
    if (i % 17 == 0) 
     m17Multiples++; 
} 
private void calc23Primitive(int i) { 
    if (i % 23 == 0) 
     m23Multiples++; 
} 



/// using object wrappers 
class IntWrapper { 
    private int mInt; 
    public IntWrapper(int i) { 
     mInt = i; 
    } 
    public int getInt() { 
     return mInt; 
    } 
    public void increment() { 
     mInt++; 
    } 
} 
public int methodObject(int i) { 

    IntWrapper o17 = new IntWrapper(0); 
    IntWrapper o23 = new IntWrapper(0); 

    calc17Object(i, o17); 
    calc23Object(i, o23); 

    return o17.getInt() + o23.getInt(); 
} 

private void calc17Object(int i, IntWrapper o) { 
    if (i % 17 == 0) 
     o.increment(); 
} 
private void calc23Object(int i, IntWrapper o) { 
    if (i % 23 == 0) 
     o.increment(); 
} 


public static void main(String[] args) { 
    TestSharedMembers t = new TestSharedMembers(); 
    final int NUM_ITERS = 20000000; 

    double start = System.currentTimeMillis(); 
    int total = 0; 
    for (int i=0; i<NUM_ITERS; i++) { 
     total += t.methodPrimitive(i); 
    } 
    double stop = System.currentTimeMillis(); 
    System.out.println(total); 
    System.out.println(stop - start); 

    start = System.currentTimeMillis(); 
    total = 0; 
    for (int i=0; i<NUM_ITERS; i++) { 
     total += t.methodObject(i); 
    } 
    stop = System.currentTimeMillis(); 
    System.out.println(total); 
    System.out.println(stop - start); 

} 
} 

EDIT 2: Еще один пример чтобы проверить теорию Джона.

public class TestSharedMembers { 

protected int m17Multiples; 
protected int m23Multiples; 

public int methodPrimitive(int i) { 

    m17Multiples = 0; 
    m23Multiples = 0; 

    calc17Primitive(i); 
    calc23Primitive(i); 

    return m17Multiples + m23Multiples; 
} 

private void calc17Primitive(int i) { 
    if (i % 17 == 0) 
     m17Multiples++; 
    if (i % 19 == 0) 
     m17Multiples++; 
    if (i % 5 == 0) 
     m17Multiples++; 
    if (i % 43 == 0) 
     m17Multiples++; 
    if (i % 41 == 0) 
     m17Multiples++; 
    if (i % 91 == 0) 
     m17Multiples++; 
} 
private void calc23Primitive(int i) { 
    if (i % 23 == 0) 
     m23Multiples++; 
    if (i % 17 == 0) 
     m23Multiples++; 
    if (i % 19 == 0) 
     m23Multiples++; 
    if (i % 5 == 0) 
     m23Multiples++; 
    if (i % 43 == 0) 
     m23Multiples++; 
    if (i % 41 == 0) 
     m23Multiples++; 
    if (i % 91 == 0) 
     m23Multiples++; 
} 



/// using object wrappers 
class IntWrapper { 
    private int mInt1; 
    private int mInt2; 
    public IntWrapper(int i1, int i2) { 
     mInt1 = i1; 
     mInt2 = i2; 
    } 
    public int getInt1() { 
     return mInt1; 
    } 
    public int getInt2() { 
     return mInt2; 
    } 
    public void increment1() { 
     mInt1++; 
    } 
    public void increment2() { 
     mInt2++; 
    } 
} 
public int methodObject(int i) { 

    IntWrapper o = new IntWrapper(0,0); 

    calc17Object(i, o); 
    calc23Object(i, o); 

    return o.getInt1() + o.getInt2(); 
} 

private void calc17Object(int i, IntWrapper o) { 
    if (i % 17 == 0) 
     o.increment1(); 
    if (i % 19 == 0) 
     o.increment1(); 
    if (i % 5 == 0) 
     o.increment1(); 
    if (i % 43 == 0) 
     o.increment1(); 
    if (i % 41 == 0) 
     o.increment1(); 
    if (i % 91 == 0) 
     o.increment1(); 
} 
private void calc23Object(int i, IntWrapper o) { 
    if (i % 23 == 0) 
     o.increment2(); 
    if (i % 17 == 0) 
     o.increment1(); 
    if (i % 19 == 0) 
     o.increment1(); 
    if (i % 5 == 0) 
     o.increment1(); 
    if (i % 43 == 0) 
     o.increment1(); 
    if (i % 41 == 0) 
     o.increment1(); 
    if (i % 91 == 0) 
     o.increment1(); 
} 


public static void main(String[] args) { 
    TestSharedMembers t = new TestSharedMembers(); 
    final int NUM_ITERS = 20000000; 

    double start = System.currentTimeMillis(); 
    int total = 0; 
    for (int i=0; i<NUM_ITERS; i++) { 
     total += t.methodPrimitive(i); 
    } 
    double stop = System.currentTimeMillis(); 
    System.out.println(total); 
    System.out.println(stop - start); 

    start = System.currentTimeMillis(); 
    total = 0; 
    for (int i=0; i<NUM_ITERS; i++) { 
     total += t.methodObject(i); 
    } 
    stop = System.currentTimeMillis(); 
    System.out.println(total); 
    System.out.println(stop - start); 

} 
} 
+0

Почему решение оберточного объекта было бы медленнее, чем решение переменной-члена? – irreputable

+1

Я думаю, что это из-за создания и уничтожения объектов. Я знаю, что это медленнее, хотя, потому что я его протестировал. – Jonah

ответ

1

Вместо упаковки отдельных примитивов или добавление примитивы как поля в классе метод находится, не могли бы вы вместо того, чтобы сделать этот метод сам объект с примитивными полями?

Если класс, где вы в настоящее время method() определен в Foo, а не добавлять примитивы, которые он использует, чтобы Foo сделать еще один класс (внутренний класс, возможно) FooMethod и, где бы вы назвали method() раньше, делать new FooMethod().method() вместо этого. Тогда примитивные поля в FooMethod будут эффективными локальными переменными, так как объект FooMethod может быть GC GC после завершения вызова метода.

+0

Я верю, что это именно то, что он предлагает в заключительном параграфе: «Будет ли подходящим внутренний класс, обертывающий метод, под-методы и примитивные данные?» –

+0

@ Jon Вы правы, я, кажется, застеклен над этой линией. – ColinD

2

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

Нужны ли подтемы для доступа к состоянию в существующем классе? Если это так, я бы, вероятно, сохранил подтемы в существующем классе - я подозреваю, что будет держать код более четким. Я не поклонник истинных «внутренних» классов (с неявной ссылкой на внешний класс). Обычно я делаю свои вложенные классы статичными и делаю все явно.

(Обратите внимание, что «объекты передаются по ссылке» - это неверное утверждение о том, что происходит на Java. Реальность заключается в том, что ссылки передаются по значению, которое является тонко различным. В Java нет пропусков, эталонная семантика.)

+0

Джон, спасибо за ваш ответ. Если я решил сохранить методы там, где они есть, и передать экземпляр класса состояний, то я предполагаю, что рассматриваемый экземпляр должен быть переменной-членом моего существующего (родительского) класса. Но с точки зрения дизайна, разве у нас еще нет члена, который существует, чтобы не сохранять состояние объекта, а только чтобы помочь реализовать методы? – Jonah

+0

@ Jonah: Зачем вам нужно иметь его как переменную экземпляра? Создайте его как локальную переменную в 'method()', а затем передайте ссылку в качестве аргумента в 'submethod1()', 'submethod2()' и т. Д. –

+0

@Jon: Разве это не то, что Иона заявил в своем OP замедляя его симуляцию? – chiccodoro

1
public class ShareLocal { 

private final static class methods 
{ 

    private int sharedInt; 
    public void method(int init) 
    { 
    sharedInt=init; 
    ... 
    } 
    private void meth1() 
    { 
    ... 
    } 
    private void meth2() 
    { 
    ... 
    } 
} 
private ShareLocal.methods methodCall=new ShareLocal.methods(); 
void simulationLoop() 
{ 
for(int i=0;i<10;i++) 
    methodCall.method(i); 
} 


} 

Я думаю, что внутренний класс, как вы упомянули, является лучшим способом.

0

Если вы действительно считаете, что потеря производительности из-за объекта-обертки является проблемой, я бы рекомендовал не извлекать упомянутые 2 субметода. Большие методы - плохой запах, но если вам это нужно для моделирования, тогда «красивый» объектно-ориентированный дизайн может быть вторым приоритетом.

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

+0

chiccodoro, один длинный метод, несомненно, ответит на мои проблемы с производительностью, но я не думаю, что разрушение методов создает какой-либо значительный успех, пока мы не создаем объекты-обертки.Я не тестировал это заявление, но я думаю, что это правда. Большая проблема: как-то кажется неправильным, что правильное проектирование исключает хорошую производительность. Это означает, что я еще не нашел правильного решения или что Java в этом отношении ограничен. В любом случае проблема вынуждает меня проектировать с учетом реализации, что не очень хорошо ... – Jonah

0

Возможно, вы могли бы объяснить еще раз, почему для вас так важно, чтобы поля исчезли? Если производительность важна, как вы говорите, зачем добавить это как «требование»? Кажется, у вас уже есть решение, которое позволяет избежать стоимости создания объекта.

Если вы хотите получить более оптимальное решение, используйте аргументы и возвращаемые значения.

public class TestSharedMembers { 
    public int methodPrimitive(int i) { 
    return calc17Primitive(i) + calc23Primitive(i); 
    } 
    private int calc17Primitive(int i) { 
    return i % 17 == 0 ? 1 : 0; 
    } 
    private void calc23Primitive(int i) { 
    return i % 23 == 0? 1 : 0; 
    } 
} 
+0

Питер, вспомните, что мой пример был упрощенной версией реальной проблемы, которая была бы слишком длинной и громоздкой для вставьте. Ваше решение действительно было бы именно тем, что я сделал бы, если бы каждый под-метод должен был возвращать только одно значение. Проблема в том, что в реальной задаче каждый субметод должен был возвращать как минимум 2 значения, а затем нам нужно было возвращать объекты или массивы, и мы вернулись в одну лодку. – Jonah

+0

Тогда вам нужно использовать поля. Возможно, вы могли бы дать больше разъяснений относительно того, что вы беспокоитесь об этом? –

+0

BTW, длинный может содержать две значения int, но это уродливое решение, которое вряд ли поможет вам во всех случаях. –

Смежные вопросы