我有两个类*,我不确定如何称呼它们之间的关系。 ClassA 的对象类似于 ClassB 的对象的类(“原型”或“模式”)。
Song
有标题和艺术家。SongDimension
( ClassA )具有一个标签(例如“流派”;“响度”)和一组AcceptableValue
的标签(例如“经典摇滚”, “古典”,“雷鬼”,“ 9”,“ 10”,“ 11”)。Song
有一组SongDimensions
,对于每个SongDimension
,它恰好有一个SongDimensionValue
( ClassB ),该AcceptableValue
的哪个SongDimension
适用于此Song
。我正在尝试使用这种模式(或替代方法,如果这不是一个好的模式)来寻找最佳实践,但是我不知道该怎么称呼。在我看来,SongDimension
的对象几乎就像SongDimensionValue
的对象的类。但是,当我使用这样的术语搜索Internet时,只会得到有关什么是类和对象的基本文章。
此模式有名称吗?
*我正在使用Java术语(类,对象),但是此模式可以在任何OOP语言中应用。
编辑:谢谢大家的回答。阅读它们,我意识到我忘了提到一个重要的要求。我希望用户能够在运行时创建自己的SongDimension
实例,而我在编译时无法预测这些实例。
例如,系统的一个用户可能不愿意使用流派SongDimension
在其库中对Songs
进行分类。而是使用标签“颜色”创建自己的SongDimension
,并为其提供可接受的值,例如红色,黄色和黑色。这个新的SongDimension
对其他用户可能没有任何意义,但对他们来说却是有意义的,这就是我要允许的灵活性。他们仍然可以独立于流派而使用响度SongDimension
。
如果不是这个要求,枚举和继承确实是可行的方法。如果不首先包含它,那对我来说是不好的。
答案 0 :(得分:0)
听起来您正在寻找枚举。
SongDimension
是一个界面。 Genre
,Loudness
等是实现接口的枚举类。可接受的值定义为各个枚举的实例。
这很适合您的描述:SongDimension
的具体实现是枚举类,其实例对象定义了可接受值的范围。 Java特别适合这种设计,因为Java的枚举类是一类公民,它像其他任何类一样支持状态和行为。
这不是一种设计模式,但是它是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();
}
}
}
答案 1 :(得分:0)
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>
(肯定是天真的实现。例如不检查null):
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");
}
答案 2 :(得分:0)
我尝试提供不需要任何Enum
的解决方案,因为您正在寻找:
我使用Java术语(类,对象),但是这种模式可以在任何OOP语言中应用。
给定的问题可能包括逻辑错误:
一首歌有一套SongDimensions
[...]
SongDimension(ClassA)具有标签(例如“流派”;“响度”)
这意味着Song
对象可能同时具有多个具有不同响度的不同流派。
但是,如果您在代码中思考,那么您真正想说的是new Song("mj", "billie jean", new Rock(), new LoudnessLevel8())
;
但是在给定的问题中, Billie jean 可能是响度 6 的 reggae ,而同时是 >摇滚,响度为8
。
如果我错了,请在评论中纠正我。
一首歌有标题和歌手。一首歌曲具有
一组SongDimensions。
class Song {
private String title;
private String artist;
private 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
..在评论中纠正我
答案 3 :(得分:0)
这听起来像power type。也许尝试这样的事情:
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));
}
}