dal*_*ons 13 java static interface
我正在为我编写的Java程序制作一个迷你ORM ...我的数据库中的每个表都有一个类,都继承自ModelBase.
ModelBase 是抽象的,提供了一堆静态方法,用于从db中查找和绑定对象,例如:
public static ArrayList findAll(Class cast_to_class) {
//build the sql query & execute it
}
Run Code Online (Sandbox Code Playgroud)
所以你可以做一些事情,比如ModelBase.findAll(Albums.class)获得所有持久专辑的列表.我的问题是,在这个静态上下文中,我需要从具体类Album中获取相应的sql字符串.我不能有像这样的静态方法
public class Album extends ModelBase {
public static String getSelectSQL() { return "select * from albums.....";}
}
Run Code Online (Sandbox Code Playgroud)
因为Java中没有静态方法的多态性.但我不想创建getSelectSQL()一个实例方法,Album因为我需要创建一个实例,只是为了获得一个在行为上非常静态的字符串.
目前,findAll()使用反射来获取相关类的相应sql:
select_sql = (String)cast_to_class.getDeclaredMethod("getSelectSql", new Class[]{} ).invoke(null, null);
Run Code Online (Sandbox Code Playgroud)
但这非常糟糕.
那么任何想法?这是我一次又一次的一般问题 - 无法在类或接口中指定抽象静态方法.我知道为什么静态方法多态不会也无法工作,但这并不能阻止我再次使用它!
是否有任何模式/构造允许我确保具体的子类X和Y实现一个类方法(或者失败,类常量!)?
尽管如此,我完全同意“在这里使用静态是错误的”这一点,我有点理解你在这里试图解决的问题。仍然实例行为应该是工作方式,但如果你坚持这就是我会做的:
从您的评论开始“我需要创建它的实例只是为了获得行为真正静态的字符串”
这并不完全正确。如果你仔细观察,你会发现你并没有改变基类的行为,只是改变了方法的参数。换句话说,你改变的是数据,而不是算法。
当新的子类想要更改方法的工作方式时,继承更有用,如果您只需要更改类用于工作的“数据”,可能像这样的方法就可以解决问题。
class ModelBase {
// Initialize the queries
private static Map<String,String> selectMap = new HashMap<String,String>(); static {
selectMap.put( "Album", "select field_1, field_2 from album");
selectMap.put( "Artist", "select field_1, field_2 from artist");
selectMap.put( "Track", "select field_1, field_2 from track");
}
// Finds all the objects for the specified class...
// Note: it is better to use "List" rather than "ArrayList" I'll explain this later.
public static List findAll(Class classToFind ) {
String sql = getSelectSQL( classToFind );
results = execute( sql );
//etc...
return ....
}
// Return the correct select sql..
private static String getSelectSQL( Class classToFind ){
String statement = tableMap.get( classToFind.getSimpleName() );
if( statement == null ) {
throw new IllegalArgumentException("Class " +
classToFind.getSimpleName + " is not mapped");
}
return statement;
}
}
Run Code Online (Sandbox Code Playgroud)
也就是用一个Map来映射所有的语句。“明显”的下一步是从外部资源加载地图,例如属性文件、xml 甚至(为什么不)数据库表,以获得额外的灵活性。
这样您就可以让您的类客户(和您自己)满意,因为您不需要“创建实例”来完成这项工作。
// Client usage:
...
List albums = ModelBase.findAll( Album.class );
Run Code Online (Sandbox Code Playgroud)
...
另一种方法是从后面创建实例,并在使用实例方法时保持客户端接口完整,这些方法被标记为“受保护”以避免外部调用。您也可以按照与上一个示例类似的方式执行此操作
// Second option, instance used under the hood.
class ModelBase {
// Initialize the queries
private static Map<String,ModelBase> daoMap = new HashMap<String,ModelBase>(); static {
selectMap.put( "Album", new AlbumModel() );
selectMap.put( "Artist", new ArtistModel());
selectMap.put( "Track", new TrackModel());
}
// Finds all the objects for the specified class...
// Note: it is better to use "List" rather than "ArrayList" I'll explain this later.
public static List findAll(Class classToFind ) {
String sql = getSelectSQL( classToFind );
results = execute( sql );
//etc...
return ....
}
// Return the correct select sql..
private static String getSelectSQL( Class classToFind ){
ModelBase dao = tableMap.get( classToFind.getSimpleName() );
if( statement == null ) {
throw new IllegalArgumentException("Class " +
classToFind.getSimpleName + " is not mapped");
}
return dao.selectSql();
}
// Instance class to be overrided...
// this is "protected" ...
protected abstract String selectSql();
}
class AlbumModel extends ModelBase {
public String selectSql(){
return "select ... from album";
}
}
class ArtistModel extends ModelBase {
public String selectSql(){
return "select ... from artist";
}
}
class TrackModel extends ModelBase {
public String selectSql(){
return "select ... from track";
}
}
Run Code Online (Sandbox Code Playgroud)
而且你不需要改变客户端代码,仍然拥有多态性的力量。
// Client usage:
...
List albums = ModelBase.findAll( Album.class ); // Does not know , behind the scenes you use instances.
Run Code Online (Sandbox Code Playgroud)
...
我希望这有帮助。
关于使用 List 与 ArrayList 的最后一点说明。对接口进行编程总是比对实现进行编程更好,这样可以使代码更加灵活。您可以使用另一个更快的 List 实现,或者执行其他操作,而无需更改客户端代码。