我有一个与许多相互依赖的项目:
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)
i具有core
和core_web
中定义的典型MVC作品:服务,控制器,存储库,JPA实体等。
app1
项目使用所有这些核心内容,除了使用application.properties
的某些特定配置之外,我将能够使用一些@Configuration
Magic来锻炼。
但是,app2
项目将Date
字段和List
字段添加到实体中。到处都是级联的效果:DTO,工厂,控制器,服务等。从设计的角度来看,它只是一个混乱的遗产混乱。是否有更好,干燥,更好的实践方法来处理此操作,或者我只需要继承我的核心代码几乎每个班级才能将几个字段添加到一个实体中?
作为一个简单的示例(在这里使用lombok以使其简洁和令人敬畏),这是core
的@Entity
:
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
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
类继承?如您所见,我得到了这个湿滑的斜坡,即继承的类,这些类仅用于将一个或两个字段的逻辑添加到所有应该能够在某个时候一般处理它们的类中。我的意思是,这就是多态性的目的,对吗?这样我就可以在整个地方和服务等上使用Account
。"只是工作?"这个纠结的混乱中是否有适配器模式可以工作的地方?预先感谢您的任何建议。
继承是一个滑坡。我建议您考虑不要扩展Account
,毕竟您只添加一个字段,几乎不值得为此创建更多的类。对我来说,Bride
并不像Account
,也许BrideAccount
是一个更好的名称。但是您可以使用构图:
@Entity
public class BrideAccount {
@ID
Integer id;
@Column(name = "WEDDING_DATE")
private Date weddingDate;
Account account;
}
您不必扩展所有其他控制器等。
其他选项是制作帐户的接口,并让所有这些控制器使用接口并仅扩展您需要的内容。
,但我强烈建议您不需要添加课程。如果您可以将字段添加到Account
类中,那只是每个帐户在超级设置类中不使用的,这是可取的IMO。您只需要在使用哪些字段和不使用什么字段时就非常谨慎。另一个字段,例如AccountType
,也许需要为此目的告诉您它是什么类型的帐户,那么您将知道是否正在使用婚礼领域。CreateAccountCommand
也需要这一点。我在这里回答了一个类似的问题:动态构造组成对象。通常,我默认不创建更多类,因为我从复杂类型层次结构的经验中偏见,创造了比他们解决的更多问题。