2013-10-11 3 views
7

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

public static String makeStatement(URI subject, URI predicate, String object) { 
    return " " + encode(subject) + " " + encode(predicate) + " " + encode(object) + ".\n"; 
} 

public static String makeStatement(String subject, URI predicate, String object) { 
    return " " + encode(subject) + " " + encode(predicate) + " " + encode(object) + ".\n"; 
} 

public static String makeStatement(URI subject, URI predicate, Literal object) { 
    return " " + encode(subject) + " " + encode(predicate) + " " + encode(object) + ".\n"; 
} 

private static String encode(String binding) { 
    return "?" + binding; 
} 

private static String encode(URI uri) { 
    return "<" + uri.stringValue() + ">"; 
} 

private static String encode(Literal literal) { 
    return "\"" + literal.stringValue() + "\"" + literal.getDatatype(); 
} 

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

Обычно я бы ответил на такой вопрос предложением создать суперкласс, но я не могу редактировать String, URI и Literal. Другим вариантом было бы определить

public static String makeStatement(Object subject, Object predicate, Object object) { 
    String encodedSubject = "", encodedPredicate = "", encodedObject = ""; 
    if (subject.getClass().equals(URI.class)) { 
     encodedSubject = encode((URI) subject); 
} 
    return " " + encode(encodedSubject) + " " + encode(encodedPredicate) + " " + encode(encodedObject) + ".\n"; 
} 

, а затем проверить классы для каждого аргумента, но я считаю, что это не очень элегантно. Еще одно предложение - определить что-то вроде makeStatement (URI subjectURI, String subjectString, Literal subjectLiteral, URI predicateURI .. и т. Д.), А затем проверить, какие аргументы являются нулевыми и идти оттуда, но это будет означать набор большого количества нулей при вызове функция. Третьим вариантом будет https://stackoverflow.com/a/12436592/1014666, но опять-таки это требует некоторого дополнительного ввода при вызове функции makeStatement.

Любые предложения?

+0

Сотрудник столкнулся с аналогичной проблемой некоторое время назад, и он написал скрипт Python, который записывает все 9 комбинаций в виде текстового файла .java. – mbatchkarov

+0

Nice, но не очень элегантный :) – Rhand

+0

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

ответ

3

Вы можете использовать шаблон строитель:

public class StatementMaker { 
    private static String encode(String binding) { 
     return "?" + binding; 
    } 

    private static String encode(URI uri) { 
     return "<" + uri.stringValue() + ">"; 
    } 

    private static String encode(Literal literal) { 
     return "\"" + literal.stringValue() + "\"" + literal.getDatatype(); 
    } 

    public static Statement from(String b) { 
     return new Statement(encode(b)); 
    } 

    public static Statement from(URI b) { 
     return new Statement(encode(b)); 
    } 

    public static Statement from(Literal b) { 
     return new Statement(encode(b)); 
    } 

    public static class Statement { 

     private StringBuilder buf; 
     private Statement(String s) { 
      buf = new StringBuilder(" "); 
      buf.append(s); 
     } 

     public Statement with(String s) { 
      buf.append(" ").append(encode(b)); 
      return this; 
     } 

     public Statement with(URI s) { 
      buf.append(" ").append(encode(b)); 
      return this; 
     } 

     public Statement with(Literal s) { 
      buf.append(" ").append(encode(b)); 
      return this; 
     } 

     public String toString() { 
      return buf.toString() + ".\n"; 
     } 

    } 
} 

Теперь вы можете построить заявление как например:

StatementMaker.from(subject).with(predicate).with(object).toString()

В коде, который нуждается заявления вы можете сократить код дополнительно со статическим импорта:

import static my.package.StatementMaker.from;

Тогда утверждение сводится к:

from(subject).with(predicate).with(object).toString()

К внутреннему классу можно добавить еще 3 метода:

public static class Statement { 

    private StringBuilder buf; 
    private Statement(String s) { 
     buf = new StringBuilder(" "); 
     buf.append(s); 
    } 

    public Statement with(String s) { 
     buf.append(" ").append(encode(b)); 
     return this; 
    } 

    public Statement with(URI s) { 
     buf.append(" ").append(encode(b)); 
     return this; 
    } 

    public Statement with(Literal s) { 
     buf.append(" ").append(encode(b)); 
     return this; 
    } 

    public String and(String s) { 
     buf.append(" ").append(encode(b)); 
     return buf.toString() + ".\n"; 
    } 

    public String and(URI s) { 
     buf.append(" ").append(encode(b)); 
     return buf.toString() + ".\n"; 
    } 

    public String and(Literal s) { 
     buf.append(" ").append(encode(b)); 
     return buf.toString() + ".\n"; 
    } 


    public String toString() { 
     return buf.toString() + ".\n"; 
    } 

} 

Затем вы можете использовать избежать toString() вызова, как это:

String statement = from(subject).with(predicate).and(object);

+0

Мне нравится этот ответ, поскольку makeStatement становится намного яснее, однако toString at конец каждой строки менее приятный. – Rhand

+0

После этого вам даже не нужны makeStatement. Если вы хотите избежать, можете добавить 3 метода в внутренний класс Statement. Я добавлю их, чтобы ответить. –

+0

Добавлен способ избежать вызова toString(). –

2

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

Так что забудьте о каждом возможном выборе и сделайте самые доступные.

+0

К сожалению, нет простого способа конвертировать из одного в другой. Для программного обеспечения, в котором я сейчас работаю, я нахожу себя в написании функций, из которых 80% строк могут состоять из вызовов makeStatement. – Rhand

+0

Нет простого способа конвертировать из URI в String и наоборот? – Kayaman

+0

Уверен, что есть простой способ конвертировать из URI в строку, но в моем контексте я хочу обернуть URI с помощью <> и поставить? перед строкой. – Rhand

1

Обычно я бы ответить на этот вопрос с предложением создать суперкласса, но я не могу редактировать String, URI и литералов. Другим вариантом было бы определить

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

public class LiteralWrapper { 
    private String string = null; 
    private URI uri = null; 
    private Literal literal = null; 

    public LiteralWrapper(String sting) { 
     this.string = string; 
    } 

    public LiteralWrapper(URI uri) { 
     this.uri = uri; 
    } 

    public LiteralWrapper(Literal literal) { 
     this.literal = literal; 
    } 

    // Note that this class is immutable, 
    // so you know you cannot have more than one non-null member. 
    // Probably not a bad idea to add some getters, though. 


    /* The encode functions from your original question */ 

    private static String encode(String binding) { 
     return "?" + binding; 
    } 

    private static String encode(URI uri) { 
     return "<" + uri.stringValue() + ">"; 
    } 

    private static String encode(Literal literal) { 
     return "\"" + literal.stringValue() + "\"" + literal.getDatatype(); 
    } 

    @Override 
    public String toString() { 
     if (literal != literal) { 
      return encode(literal); 
     } 
     if (uri != null) { 
      return encode(uri); 
     } 
     return encode(string); 
    } 
} 

makeStatement Теперь ваш код становится тривиальным:

public static String makeStatement(LiteralWrapper subject, LiteralWrapper predicate, LiteralWrapper object) { 
    return " " + subject + " " + predicate + " " + object + ".\n"; 
} 

EDIT:
В соответствии с комментарием ниже, это делает вызов makeStatement немного раздражает. Вместо того, чтобы делать makeStatement(myString, myUri, myLiteral), вы можете позвонить по телефону makeStatement(new LiteralWrapper(myString), new LiteralWrapper(myUri), new LiteralWrapper(myLiteral)). Эта проблема не может быть полностью избежать с данным решением, но оно может быть смягчено путем введения некоторых синтаксический сахар в виде фабричных методов:

public static LiteralWrapper wrap(String string) { 
    return new LiteralWrapper(string); 
} 

public static LiteralWrapper wrap(URI uri) { 
    return new LiteralWrapper(uri); 
} 

public static LiteralWrapper wrap(Literal literal) { 
    return new LiteralWrapper(literal); 
} 

Теперь вы можете импортировать эти статически оберток везде, где вам нужно использовать makeStatement:

import static org.some.package.LiteralWrapper.wrap; 
/* other imports*/ 

public class MyClass { 
    public void someFunction() { 
     /* some business logic */ 

     makeStatemet(wrap(myString), wrap(myURI), wrap(myLiteral)); 
    } 
} 
+0

Спасибо за ваш ответ, это похоже на ответ, на который я ссылался в своем вопросе. Пока это самый элегантный вариант, однако это превратило бы makeArtment («subject», «predicate», «object») в makeStatement (новый StatementWrapper («subject»), новый StatementWrapper («предикат»), StatementWrapper (" объект ")); что на самом деле не улучшает читаемость. – Rhand

+0

@ Rhand Я согласен, что это несколько затрудняет читаемость. См. Мое редактирование для ответа выше, чтобы устранить проблему. IMHO, решение приводит к довольно легко читаемому коду, но все это в глазах смотрящего. – Mureinik

+0

Мне нравится функция обертывания (это расширено некоторыми другими ответами) – Rhand

1
public static String makeStatement(Object subject, Object predicate, Object object) { 
    return " " + encode(subject) + " " + encode(predicate) + " " + encode(object) + ".\n"; 
} 

private static String encode(Object obj) { 
    String encodedOj =""; 
    if (obj.getClass().equals(URI.class)) { 
     encodedOj = encode((URI) obj); 
    }else if(obj.getClass().equals(Literal.class)){ 
     encodedOj = encode((Literal) obj); 
    }else if(obj.getClass().equals(String.class)){ 
     encodedOj = encode((String) obj); 
    } 
    return encodedOj; 
} 

private static String encode(String binding) { 
    return "?" + binding; 
} 

private static String encode(URI uri) { 
    return "<" + uri.stringValue() + ">"; 
} 

private static String encode(Literal literal) { 
    return "\"" + literal.stringValue() + "\"" + literal.getDatatype(); 
} 
+0

Знаете ли вы, есть ли хит производительности для проверки класса каждого объекта obj? По сравнению с ответом У Мэда это кажется главным отличием (и функция makeStatement сильно отличается). – Rhand

+0

Спасибо за ваш ответ, это сработает, но я предпочитаю ответ У Мада – Rhand

+1

Проблема в том, что любой объект может быть передан. Это сам по себе запах кода, но что еще хуже, если 'makeStatement' вызывается с чем-то, что а не 'URI',' Literal' или 'String', метод' encode (Object) 'терпит неудачу и возвращает пустую строку. Вы должны хотя бы добавить 'else throw new IllegalArgumentException' в этот блок if. –

0

Вы можете создавать весело и легко использовать обертку с помощью статических фабричных методов (см Effective Java, Item 1) и анонимные классы (действующие как нечто вроде закрытий).

Вот как:

public class Item { 

    private static interface Methods { 
     public String encode(); 
    } 

    private final Methods methods; 

    private Item(Methods methods) { 
     this.methods = methods; 
    } 

    public static Item of(final String binding) { 
     return new Item(new Methods() { 
      @Override 
      public String encode() { 
       return "?" + binding; 
      } 
     }); 
    } 

    public static Item of(final URI uri) { 
     return new Item(new Methods() { 
      @Override 
      public String encode() { 
       return "<" + uri.stringValue() + ">"; 
      } 
     }); 
    } 

    public static Item of(final Literal literal) { 
     return new Item(new Methods() { 
      @Override 
      public String encode() { 
       return "\"" + literal.stringValue() + "\"" + literal.getDatatype(); 
      } 
     }); 
    } 

    public String encode() { 
     return methods.encode(); 
    } 
} 

Это очень легко добавлять новые поддерживаемые типы (который был один из ваших требований) при таком подходе: просто создать новый метод статического заводскую принимать этот тип: Item.of(NewType val).

Таким образом, вы бы метод:

public static String makeStatement(Item subject, Item predicate, Item object) { 
    return subject.encode() + " " + predicate.encode() + " " + object.encode(); 
} 

И называют это так:

makeStatement(Item.of(subject), Item.of(predicate), Item.of(object)); 

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

+0

Спасибо за ваш ответ, это сработает, но я предпочитаю ответ У Мада – Rhand

+1

Действительно, это намного лучше для вашей конкретной проблемы. Я пытался решить несколько более общий (принимая разные типы аргументов). – siledh

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