我有一个包含许多相互依赖的项目的项目:
myProject
- core
- core_web (depends on core)
- app1_core (depends on core)
- app1_web (depends on app1_core and core_web)
- app2_core (depends on core)
- app2_web (depends on app2_core and core_web)
我有core
和core_web
中定义的典型MVC片段:服务,控制器,存储库,JPA实体等。
app1
项目按原样使用所有这些核心内容 - 除了使用application.properties
的某些特定配置之外,没有任何其他内容可以与@Configuration
一起使用魔术。
但app2
项目会向实体添加Date
字段和List
字段。随处可见的附加级联的影响:DTO,工厂,控制器,服务等变成了混乱的遗传混乱,从设计的角度来看感觉很糟糕。是否有一种更好,干燥,更好的实践方法来处理这个问题,或者我只是要从我的核心代码中继承几乎每个类只是为了向实体添加几个字段?
作为一个简单示例(此处使用Lombok表示简洁和精彩),这里是来自@Entity
的{{1}}:
core
现在我在package com.example.core;
@Data
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "ACCOUNT")
@ToString(of = {"id", "emailAddress", "name"})
@EqualsAndHashCode
public class Account implements Serializable {
private static final long serialVersionUID = 1L;
@Embedded
private Address address;
@Id
@GenericGenerator(
name = "SEQ_ACCOUNT_ID",
strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
@Parameter(name = "sequence_name", value = "SEQ_ACCOUNT_ID"),
@Parameter(name = "initial_value", value = "1"),
@Parameter(name = "increment_size", value = "1")
}
)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_ACCOUNT_ID")
@Column(name = "ACCOUNT_ID", updatable = false, nullable = false, unique = true)
private Long id;
@Column(name = "EMAIL", nullable = false, length = 200, unique = true)
private String emailAddress;
public void setEmailAddress(String emailAddress) {
this.emailAddress = StringUtils.lowerCase(emailAddress);
}
@JsonIgnore
@Column(name = "PASSWORD_HASH", nullable = false, length = 256)
private String passwordHash;
@Column(name = "NAME", nullable = false, length = 200)
private String name;
@Column(name = "PHONE", nullable = false, length = 30)
private String phoneNumber;
@Column(name = "URL")
private String url;
}
app2
我期待这么多工作。它体面,优雅,不会复制很多,只要我告诉Spring不要扫描两个类的实体,它都很好。它开始变得乏味的地方是我开始添加服务代码时,例如:
package com.example.app2;
@Data
@NoArgsConstructor
@Table(name = "ACCOUNT")
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class Bride extends Account {
@Column(name = "WEDDING_DATE")
private Date weddingDate;
}
现在主要问题是: (读取:应该)我如何处理package com.example.core;
@Service
public class AccountService {
private AccountRepository accountRepository;
@Autowired
public AccountService(AccountRepository accountRepository) {
this.accountRepository = accountRepository;
}
/* the command object here would mirror Account, but contain some code
* to handle input validation and the like. I know I could just use the
* entity class, but I've been bitten by using entity classes as DTOs or
* command objects before, so I'd like to keep them separate.
*/
public Account createAccount(CreateAccountCommand createAccountCommand) {
Account account = new Account();
account.setEmailAddress(createAccountCommand.getEmailAddress());
account.setPasswordHash(createPasswordHash(createAccountCommand.getPassword()));
account.setName(createAccountCommand.getName());
account.setPhoneNumber(createAccountCommand.getPhoneNumber());
account.setUrl(createAccountCommand.getUrl());
}
}
中的额外字段?创建一个名为Bride
的新类,其扩展BrideService
并调用AccountService
?那么我现在需要一个在其中有额外字段的命令对象呢?然后验证器需要处理额外的字段,所以我继承了super.createAccount()
类?正如你所看到的,我得到了这个混乱,继承的类的滑坡,只是为了增加一个或两个字段的目的。所有类的逻辑应该能够在某些时候一般地处理它们。我的意思是,这是多态的目的,对吧?这样我就可以在整个地方和服务等处使用AccountValidator
"只是工作?"在这个纠结的混乱中,适配器模式是否有效?提前感谢任何建议。
答案 0 :(得分:1)
继承是一个滑坡。我建议你考虑不扩展Account
,你毕竟只添加一个字段,几乎不值得为此创建更多的类。 Bride
对我来说并不像Account
,也许BrideAccount
会是一个更好的名字。但是你可以改用合成:
@Entity
public class BrideAccount {
@ID
Integer id;
@Column(name = "WEDDING_DATE")
private Date weddingDate;
Account account;
}
您不必再扩展所有其他控制器等,但可以重复使用它们。
其他选项是创建一个Account接口,并让所有这些控制器使用该接口并仅扩展您需要的接口。
但我强烈建议您不要在不需要的情况下添加课程。如果你可以在Account
类中添加一个字段,这个字段就是每个帐户都没有使用,就像在超集类中那样,这是一个更好的IMO。您只需要非常小心使用哪些字段以及不使用哪些字段。可能需要另一个字段,例如AccountType
来告诉您帐户的类型,然后您就会知道婚礼字段是否正在使用中。这也需要CreateAccountCommand
。我在这里回答了类似的问题:Dynamically constructing composition object。一般来说,我默认不创建更多类,因为我偏离了我对复杂类型层次结构的体验,产生了比他们解决的更多问题。