当社のメインライブラリには、次のクラスがあります。
public class User {
private long id;
private UserType type;
// Getters, setters, constructors, etc.
}
UserType
を実装するためのいくつかのオプションがあります。その1つがenum
です。
public enum UserType {
DEFAULT (10),
TYPE_A (20);
private int type;
UserType(int type) {
this.type = type;
}
public int getType() {
return type;
}
public static UserType valueOf(int value){
for(UserType type : values()){
if(type.getType() == value){
return type;
}
}
throw new IllegalArgumentException(String.format("Couldn't convert value : %d into a valid %s object", value, UserType.class));
}
}
USERS
テーブル:
+----+------+
| id | type |
+----+------+
| 1 | 10 |
| 2 | 20 |
+----+------+
RowMapper<User>
再び、次のように私たちのライブラリで:
public class UserRowMapper implements RowMapper<User> {
@Override
public User mapRow(ResultSet rs, int i) throws SQLException {
return new User(rs.getLong("id"), UserType.valueOf(rs.getInt("type"));
}
}
ただし、メインライブラリを依存関係として使用するプロジェクトでは、UserType
を拡張する必要がある場合があります。 UserType
をインターフェイスに変換しても機能しません。これは、RowMapper
(ライブラリ内)がUserType
をインスタンス化する必要があるためです。
これが私が思いついたものです:
public class UserType {
private int id;
private String name;
// Getters, setters and constructors
}
そしてデータベースは次のようになります:
USERS
:
+----+------+
| id | type |
+----+------+
| 1 | 10 |
| 2 | 20 |
+----+------+
USER_TYPES
:
+----+---------+
| id | name |
+----+---------+
| 10 | DEFAULT |
| 20 | TYPE_A |
+----+---------+
RowMapper
では、次のようにユーザーを作成します。
return new User(rs.getLong("id"), new UserType(rs.getInt("type_id"), rs.getString("type_name")));
そして実用性のために、私たちはUserTypeContainer
を持つことができました:
public class UserTypeContainer {
public static final UserType DEFAULT = new UserType(10, "DEFAULT");
public static final UserType TYPE_A = new UserType(10, "TYPE_A");
}
それを利用できるように:
someUserDao.find(UserTypeContainer.DEFAULT);
そして、依存関係としてこのライブラリを持つプロジェクトは、
1)データベースに拡張型を持っている2)UserTypeContainer
を拡張して拡張型を持たせる(上の行を利用したい場合)
これは最適なソリューションですか?これは良いプログラミング習慣ですか、それとも欠陥がありますか?他にどのようなオプションがありますか?
更新:
ライブラリに含まれる再利用可能なRowMapper
を考え出す必要があります。そして、これは最適な解決策ではないと思い始めました。
オプションのUserTypeファクトリー/作成者/ビルダーをRowMapper
に取得させます。 RowMapperが慣れていないUserTypeを検出した場合は、それをファクトリに渡します(指定されている場合)。
明確にするために、私はUserTypeをクラスにしますが、それでも列挙型のように見える可能性があります。
class UserType {
public static final UserType DEFAULT = new UserType(10);
public static final UserType TYPE_A = new UserType(20);
private int typeId;
protected UserType(int typeId) {
this.typeId = typeId;
}
}
次に、FunctionInterfaceを引数として定義します...
@FunctionInterface
interface UserTypeCreator {
UserType create(ResultSet rs); // I don't actually recommend using ResultSet as an argument
}
次に、DAOはそのインターフェースをコンストラクターへの引数として受け取ります。
class SomeDao {
private Optional<UserTypeCreator> customTypeCreator;
public SomeDao(UserTypeCreator customTypeCreator) {
this.customTypeCreator = Optional.ofNullable(customTypeCreator);
}
public UserType create(ResultSet rs) {
int type = getType(rs);
switch (type) {
case 10:
return UserType.DEFAULT;
case 20:
return UserType.TYPE_A;
default:
return customTypeCreator
.orElseThrow(UnknownUserTypeException::new)
.create(rs);
}
}
}