Имя шаблона проектирования: объект ClassA действует как «класс» для объектов ClassB - PullRequest
0 голосов
/ 11 декабря 2018

У меня есть два класса *, и я не уверен, как назвать отношения между ними.Объекты ClassA являются подобными классами («архетипы» или «шаблоны») для объектов ClassB .

  • A Song имеет заголовоки Artist.
  • A SongDimension ( ClassA ) имеет метку (например, «Жанр»; «Громкость») и набор AcceptableValue (например, «Классический рок»)., "Классика", "Регги"; "9", "10", "11").
  • A Song имеет набор SongDimensions, и для каждого SongDimension он имеет точноодин SongDimensionValue ( ClassB ), который сообщает, какой из AcceptableValue для этого SongDimension относится к этому Song.

Я пытаюсьищите лучшие практики, используя этот шаблон (или альтернативы, если этот шаблон не годится для использования), но я не знаю, как его назвать.Мне кажется, что объекты SongDimension почти как класс для объектов SongDimensionValue.Однако, когда я ищу в Интернете такие термины, я просто получаю базовые статьи о том, что такое классы и объекты.

Есть ли название для этого шаблона?

* Я использую Javaтермины (класс, объект), но этот шаблон может применяться на любом языке ООП.

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

Например, один пользователь системы может не захотеть использовать жанр SongDimension для классификации Songs в своей библиотеке.Вместо этого они создают свои собственные SongDimension с меткой «Цвет» и присваивают им приемлемые значения, такие как красный, желтый и черный.Этот новый SongDimension может не иметь никакого значения для других пользователей, но он имеет для них значение, и я хочу предоставить такую ​​гибкость.Они по-прежнему могут использовать громкость SongDimension независимо от жанра.

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

Ответы [ 4 ]

0 голосов
/ 11 декабря 2018

Это звучит почти как тип питания .Может быть, попробовать что-то вроде этого:

import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
class Artist {}
class Song {
    Song(String title,Artist artist,Map<Dimensions,Object> dimensionToValue) {
        this.title=title;
        this.artist=artist;
        this.dimensionToValue=dimensionToValue;
    }
    void setDimensionValue(Dimensions dimension,Object object) {
        try {
            if(dimensionToValue.containsKey(dimension)) {
                if(dimension.dimensionValues.contains(object)) {
                    System.out.println("set "+dimension+" to "+object);
                    dimensionToValue.put(dimension,object);
                }
                else throw new RuntimeException(object+" is illegal!");
            } else throw new RuntimeException(dimension+"is illegal dimension! for this song?");
        } catch(Exception e) {
            System.out.println("caught: "+e);
        }
    }
    Object getDimensionValue(Dimensions dimension) {
        return dimensionToValue.get(dimension);
    }
    String title;
    Artist artist;
    final Map<Dimensions,Object> dimensionToValue;
}
class Data {
    static final String[] genreValuesArray=new String[] {"country","rock"};
    static final Integer[] loudnessValuesArray=new Integer[] {1,2,3};
    static final Set<Object> genreValues=new LinkedHashSet(Arrays.asList(genreValuesArray));
    static final Set<Object> loudnessValues=new LinkedHashSet(Arrays.asList(loudnessValuesArray));
}
enum Dimensions {
    genre(Data.genreValues),loudness(Data.loudnessValues);
    Dimensions(Set<Object> dimensionValues) {
        this.dimensionValues=dimensionValues;
    }
    final Set<Object> dimensionValues;
}
public class So53724633_design_pattern_name_object_of_classa_acts_like_a_class_for_objects_of_classb {
    public static void main(String[] args) {
        Map<Dimensions,Object> map=new LinkedHashMap<>();
        Dimensions dimension=Dimensions.genre;
        map.put(dimension,null);
        Song song1=new Song("foo",new Artist(),map);
        Object value=song1.getDimensionValue(dimension);
        System.out.println("value of "+dimension+" is: "+value);
        song1.setDimensionValue(Dimensions.genre,Data.genreValuesArray[0]);
        value=song1.getDimensionValue(dimension);
        System.out.println("value of "+dimension+" is: "+value);
        song1.setDimensionValue(Dimensions.genre,"classical");
        song1.setDimensionValue(Dimensions.loudness,Integer.valueOf(1));
    }
}
0 голосов
/ 11 декабря 2018

TL; DR;Вы, кажется, описываете общую проблему наследования.Существует концепция SongDimension, и есть другие концепции, которые разделяют тот факт, что они являются подтипами SongDimension.Т.е. громкость - это измерение песни, как темп, жанр и т. Д. Поиск полиморфизма и наследования.

Теперь перейдем к более длинному ответу.

Огромный отказ от ответственности!

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

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


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

Я бы подумал об этой проблеме следующим образом:

Есть класс Song.Каждая песня является экземпляром этого класса.

Класс песни содержит коллекцию объектов типа SongDimension:

public class Song {
    public string title;
    public string artist;
    public List<SongDimension> songDimensions;
}

Класс SongDimension является абстрактным классом со следующей сигнатурой

public abstract class SongDimension {
    public abstract string GetValue();
}

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

public enum Genres {
    ClassicRock, Classical, Reggae
}


public class Genre extends SongDimension {
    private Genres _genre;

    public string GetValue(){
        switch(_genre) {
           case ClassicRock: return "Classic Rock";
           default: return _genre.name();
        }
    }

    public Genre(Genres whichGenre) {
        _genre = whichGenre;
    }
}

public class Loudness extends SongDimension {
    private int _loudness;

    public string GetValue(){
        return String.valueOf(_loudness);
    }

    public int GetIntValue() {
        return _loudness;
    }

    public Loudness(int howLoud) {
        if(howLoud < 9 || howLoud > 11) throw new IllegalArgumentException();

        _loudness = howLoud;
    }

    // Add more relevant operations on the value here,
    // maybe you can take differences of loudnesses for instance?
}

Для использованиявсего этого вы можете создать пару таких песен:

Song EineKleineNachtMusik = new Song();
EineKleineNachtMusik.songDimensions.Add(new Genre(Genres.Classical));
EineKleineNachtMusik.songDimensions.Add(new Loudness(9));

Song BackInBlack = new Song();
BackInBlack.songDimensions.Add(new Genre(Genres.ClassicRock));
BackInBlack.songDimensions.Add(new Loudness(11));

for(SongDimensions d : EineKleineNachtMusik.songDimensions){
    System.out.println(d.GetValue());
}
/// prints:
/// Classical
/// 9

Самая большая ценность всего этого заключается в том, что он позволяет вам получать различную информацию о песне не как строки, а как надлежащие объекты.Риск использования строковых заполнителей для значений и типов заключается в том, что вы можете получить разные интерпретации того, что на самом деле означает «11».Даже если вы можете назначить значения «9», «10» или «11» только как «громкость», ничто не мешает вам позднее трактовать «10» как нечто совершенно иное по ошибке, например, длину песнив минутах.

Таким образом, мы можем создавать объекты, которые семантически здоровы, и включать пользовательскую логику в различные виды признаков.Например, мы могли бы реализовать класс Loudness Comparable<Loudness> (наивная реализация наверняка. Например, не проверять ноль):

public class Loudness extends SongDimension implements Comparable<Loudness> {
    ...
    // Same as before with the addition of the following:
    @Override public int compareTo(Loudness that) {
        if(_loudness < that.GetIntValue()) return -1;
        if(_loudness == that.GetIntValue()) return 0;
        if(_loudness > that.GetIntValue()) return 1;
    }  
}

Теперь я пойду на очень тонкий лед, учитывая, чтоЯ не разработчик Java.Это будет НАИБОЛЕЕ ВЕРОЯТНО содержать ошибки, поэтому, пожалуйста, не стесняйтесь бить все, что вы хотите в комментариях.Но это дополнительно иллюстрирует тот факт, что при использовании некоторого минимального стандартного шаблона можно получить сразу много преимуществ, принудительно применяя правильные значения для измерений, применяя пользовательскую соответствующую логику, различную для каждого типа измерения и т. Д.

С добавлением небольшого вспомогательного метода в классе Song для извлечения определенного типа измерения:

 public class Song {
    ...
    // same as above, with the addition of:

    public <T extends SongDimension> T GetDimension(Class<T> dimType) {
       return songDimensions.stream().filter(dim -> dim instanceof 
            dimType).Collect(Collectors.toList()).iterator().next()
    }
}

Затем мы можем сравнить две песни следующим образом:

Loudness l1 = EineKleineNachtMusik.GetDimension(Loudness.class);
Loudness l2 = BackInBlack.GetDimension(Loudness.class);

if(l1 != null && l2 != null && l1.compareTo(l2) < 0) {
   System.out.println("Yup, BackInBlack is the louder of the two");
}
0 голосов
/ 11 декабря 2018

Я пытаюсь предоставить решение, которое не нуждается в Enum, потому что вы ищете:

Я использую термины Java (Class, Object), но этот шаблон может быть примененна любом языке ООП.

Может быть, логическая ошибка?

Может быть, данная проблема включает в себя логическую ошибку:

Песня имеет набор SongDimensions
[...]
У SongDimension (ClassA) есть метка (например, «Жанр»; «Громкость»)

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

Но если вы думаете в коде, то, что вы на самом деле хотите сказать, это: new Song("mj", "billie jean", new Rock(), new LoudnessLevel8());

Но в вашей заданной проблеме это было бы возможночто Билли Джин это регги с громкостью 6 и в то же время рок с громкостью 8.

Пожалуйста, поправьте меня в комментариях, если я ошибаюсь.

Решение без логической ошибки

Песня

Песня имеет название и исполнителя.Песня имеет набор SongDimensions.

class Song {
  private String title;
  private String artist;
  private SongDimension songDimension;
}

SongDimension

Для SongDimension нам нужно Genre и Loudness.Для них мы можем создать абстрактный, а затем несколько конкретных типов.Вот пример для Genre:

interface Genre { /**/ }

class Classic implements Genre { /**/ }

class Rock implements Genre { /**/ }

Эти конкретные типы (такие как Classic и Rock) являются вашими AcceptableValue

Давайте попробуем создать новую песню:

SongDimension rockBy8= new SongDimension(new Rock(), new LoudnessLevel8());
Song billieJean = new Song("mj", "billie jean", rockBy8);

SongDimensionValue

Я думаю, что нет необходимости в SongDimensionValue .. поправьте меня в комментариях

0 голосов
/ 11 декабря 2018

Звучит так, будто вы ищете перечисление.

SongDimension - это интерфейс.Genre, Loudness и т. Д. Являются классами enum, которые реализуют интерфейс.Приемлемые значения определяются как экземпляры соответствующих перечислений.

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

Это не шаблон проектирования, но это распространенная идиома кодирования в Java.

public class Main {
    public static void main(String... args) {
        Song mySong = new Song("My Favorite Song", "My Favorite Band",
                               Genre.Classic_Rock, Loudness.Eleven);
        System.out.println(mySong);
    }

    static class Song {
        final String title;
        final String artist;
        final Collection<SongDimension> dimensions = new HashSet<>();

        Song(String title, String artist, SongDimension... dimensions) {
            this.title = title;
            this.artist = artist;
            this.dimensions.addAll(Arrays.asList(dimensions));
        }
        @Override public String toString() {
            return "Song{ title=" + title + ", artist=" + artist +
                       ", dimensions=" + dimensions + " }";
        }
    }

    interface SongDimension {
        String label();
        String value();
    }

    enum Genre implements SongDimension {
        Classic_Rock, Classical, Reggae;

        @Override public String label() {
            return getDeclaringClass().getSimpleName();
        }
        @Override public String value() {
            return name().replace('_',' ');
        }
        @Override public String toString() {
            return label() + ':' + value();
        }
    }

    enum Loudness implements SongDimension {
        Nine(9), Ten(10), Eleven(11);

        final int volume;
        Loudness(int volume) {
            this.volume = volume;
        }

        @Override public String label() {
            return getDeclaringClass().getSimpleName();
        }
        @Override public String value() {
            return String.valueOf(volume);
        }
        @Override public String toString() {
            return label() + ':' + value();
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...