2011-01-13 1 views
0
class PieceFactory {  
    @SuppressWarnings("rawtypes") 
    public Piece createPiece(String pieceType) throws Throwable{ 
     Class pieceClass = Class.forName(pieceType); 
     Piece piece = (Piece) pieceClass.newInstance(); 

     return piece;  
    } 
} 

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

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

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

+0

Почему вы не обрабатываете свои исключения на линиях, которые производят их в 'createPiece'? – justkt

+1

Гораздо лучше избегать отражения. –

+0

Спасибо всем! Я немного изменил свой код и не использовал рефлексию на этот раз. –

ответ

1

Вы пытаетесь задуматься о создании объекта Piece.

Class.forName() бросает ClassNotFoundException, в то время как Class.newInstance() бросает InstantiationException, IllegalAccessException (следовательно, почему вы должны бросить Throwable

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

class PieceFactory {  

    public Piece createPiece(String pieceType) throws Throwable{ 
     Piece piece = null; 

     if ("SubPiece1".equals(pieceType)) { 
      piece = new SubPiece1(); 
     } else if ("SubPiece2".equals(pieceType)) { 
      piece = new SubPiece2(); 
     } 

     return piece;  
    } 
} 

PS, он не проверен, просто показывает лучший способ сделать это.

H ope это помогает! :)

+1

Ваши предложения требуют прохождения класса. Как код вызова должен получить объект класса в первую очередь? –

+0

Спасибо! Извините, я не очень привык к обозначениям :). Что делает означает? и ? –

+0

@ Дэйв Коста, правда .... хорошо заметили, я прочитал его спецификацию и привязку оттуда. Я обновлю этот пост. –

1

Если вы не хотите объявлять инструкции throw или пытаться/блокировать блок везде.

Создать класс и расширить RuntimeException класс или использовать RuntimeException сам или связанные RuntimeException простирающиеся классы.

Затем поместите блок try-catch в корневой каталог (методы фабричного класса) и оберните исключения, заброшенные в тип исключения Runtime (один из выше, какой бы вы ни выбрали).

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

try{ 
    "your code which throws exception" 
} catch(ThrownExceptionType e){ 
    throw new RuntimrException(e); 
} 

Помните, что вам все равно необходимо обработать исключение. Это не значит, что все будет хорошо.

+1

Правильно, спасибо! Как обернуть исключения? :) Я действительно не понимаю, почему исключения там, в первую очередь, не должны ли они работать для создания объектов таким образом? Я имею в виду, что я пишу в блоке try-catch? –

+0

@Alex См. Выше, отредактировал сообщение. Исключение составляют исключительные случаи. Компьютер не может решить, что делать автоматически, если он не может найти класс класса Class.forName (pieceType) для загрузки. Вы должны сказать, что делать в исключительных случаях. – fmucar

+0

@Alex: Исключения хороши (всего один пример) для проблем, которые вы не можете предвидеть, например, если файл, к которому вы пытаетесь получить доступ, не существует или что-то подобное. Это хороший способ остановить поток выполнения и не пытаться сделать что-то невозможное, это ошибочно, поэтому выполнение продолжается в блоке try/catch. – Atticus

1

Оба @SuppressWarnings и throws Throwable Кольцевые колокольчики. См. Effective Java от Блоха.

+0

Я слышал об этом, кажется, хорошо. Просто нужно сначала изучить основы лучше :). Но разве это не должно быть допустимым методом для создания объектов? Почему исключения? –

1

Исключения должны быть пойманы где-то. Если я правильно понял, у вас есть один класс, который обернут вокруг этой фабрики, скажем, FactoryWrapper. Где-то в вашем коде вы используете этот класс-оболочку, и вы можете либо выбросить исключение, либо поймать его, потому что метод, в котором вы его используете, вероятно, окружен в какой-то точке (вероятно, базовым классом) блоком try/catch, в то время как другое место (которому вы передаете ссылку на FactoryWrapper), вероятно, является последним средством (это не очень хорошая практика, но это может быть метод, который никогда не называется), где исключение нужно поймать.

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

Вы можете использовать один абстрактный завод, а затем завод для каждого типа. См. Подробности here и here. Надеюсь это поможет.

EDIT:

Here интересная статья об отражении. Это даст вам представление о том, когда его использовать.

+0

Спасибо! Завод находится внутри класса Piece, в котором у меня также есть метод, который использует Factory. Является ли класс Piece оберткой тогда? Итак, что-нибудь другое, кроме использования отражения, вероятно, является лучшей альтернативой? –

+0

@Alex: да, я имел в виду ваш класс Piece в качестве обертки. Я бы только подумал, что это действительно важно. Я обновил свой ответ ссылкой, которая объясняет, когда использовать отражение. – Atticus

1

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

В этом случае тело вашего метода может бросить два проверяемые исключения: ClassNotFoundException из forname() вызова, а также InstantiationException от newInstance() вызова.

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

Но если есть основания полагать, что эти исключения могут быть разумными при нормальном использовании, вы должны подумать о том, как вы хотите их обрабатывать. Например, если имя подкласса Piece было введено пользователем в GUI (маловероятно, я знаю, но только для того, чтобы сделать точку), тогда ClassNotFoundException становится намного более вероятным, поскольку имя может быть легко ошибочно написано. В этой ситуации может иметь смысл разрешить этому методу выкинуть это исключение и потребовать, чтобы вызывающий абонент поймал и обработал его (например, предоставив сообщение пользователю, что запрошенный класс не существует).

0

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

Поскольку я проволочки в любом случае, здесь пример перечислений-подхода:

import java.util.ArrayList; 
import java.util.List; 

class PieceFactoryExample { 

    static enum PieceFactory { 
     ROOK { 
      Piece create() { 
       return new Rook(); 
      } 
     }; 
     abstract Piece create(); 
    } 

    static class Field { 
     char line; 
     int row; 

     public Field(char line, int row) { 
      this.line = line; 
      this.row = row; 
     } 

     public String toString() { 
      return "" + line + row; 
     } 
    } 

    static interface Piece { 

     void moveTo(Field field); 

     List<Field> possibleMoves(); 
    } 

    static abstract class AbstractPiece implements Piece { 

     Field field; 

     public void moveTo(Field field) { 
      this.field = field; 
     } 
    } 

    static class Rook extends AbstractPiece { 

     public List<Field> possibleMoves() { 

      List<Field> moves = new ArrayList<Field>(); 
      for (char line = 'a'; line <= 'h'; line++) { 
       if (line != field.line) { 
        moves.add(new Field(line, field.row)); 
       } 
      } 
      for (char row = 1; row <= 8; row++) { 
       if (row != field.row) { 
        moves.add(new Field(field.line, row)); 
       } 
      } 
      return moves; 
     } 
    } 

    public static void main(String[] args) { 

     Piece pawn = PieceFactory.ROOK.create(); 
     Field field = new Field('c', 3); 
     pawn.moveTo(field); 
     List<Field> moves = pawn.possibleMoves(); 
     System.out.println("Possible moves from " + field); 
     for (Field move : moves) { 
      System.out.println(move); 
     } 
    } 
} 

Я сделал все статические здесь, чтобы не усложнять пример самодостаточный. Обычно Field, Piece, AbstractPiece и Rook будут классами высшего уровня, а PieceFactory также будет топ-уровня.

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

Возвращаясь к тому, что, есть несколько подходов можно рассмотреть (на основе вашего отражения подхода):

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

Объявите «броски» в вашем методе для всех отмеченных исключений. Чтобы решить, действительно ли это, подумайте, должен ли клиент знать и понимать тип исключения, который вы бросаете. В вашем примере, должен ли клиент, который хочет создать Rook, знать и понимать InstantationException, исключение IllegalAccessException и ClassNotFoundException, созданное вашим кодом отражения? Наверное, не в этом случае.

Оберните их в исключение времени выполнения, которое не должно быть уловлено клиентами. Это не всегда хорошая идея. Тот факт, что код, который вы вызываете, выбрасывает проверенные исключения, обычно имеет причину. В вашем примере вы делали отражение, и это может пойти не так во многих отношениях (API объявляет LinkageError, ExceptionInInitializerError, ClassNotFoundException, IllegalAccessException, InstantiationException и SecurityException). Приобретение проверенных исключений в исключении времени выполнения не приводит к тому, что проблема исчезнет. Я считаю, что это «запах кода». Если ошибка означает неустранимый системный сбой, тогда это может быть допустимый выбор, но в большинстве случаев вы хотели бы обработать такие сбои более изящно.

Выполните специальное проверочное исключение для полной подсистемы. См. Например, org.springframework.dao.DataAccessException Spring, который используется для обертывания всех исключений доступа к конкретным данным для реализации. Это означает, что клиентам придется поймать только один тип исключения и может выяснить детали, если это необходимо. В вашем случае вы можете создать проверенное исключение PieceCreationException и использовать его для обертывания всех ошибок отражения. Это допустимая схема обработки исключений, но я думаю, что это может быть немного тяжело для вашего PieceFactory.

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

Возврат определенного типа ошибки. Это geeky (очень объектно-ориентированный) подход, который я видел в API ядра Java где-то некоторое время назад (черт, не помню, где). Для этого вы бы создали дополнительный кусочек типа:

class NullPiece implements Piece { 
    public void moveTo(Field field) { 
     throw new UnsupportedOperationException("The Piece could not be created"); 
    } 
    public List<Field> possibleMoves() { 
     throw new UnsupportedOperationException("The Piece could not be created"); 
    } 
} 

И возвратите экземпляр этого типа при возникновении ошибки во время создания. Преимущество заключается в том, что он более самодокументирован, чем возврат простой нулевой стоимости, но клиенты, конечно, должны будут это проверить, используя что-то вроде instanceof. Если они этого не сделают, тогда они столкнутся с отказом UnsupportedOperationException, который вызывается при вызове методов Piece. Немного тяжелый, но очень явный. Я не уверен, что я зашел так далеко, но это все еще интересная идея.