我对状态模式实现感到困惑。在这种模式下,我们应该将状态管理提取到单独的类中。乍一看,它让我们避免了大麻烦 if ... else ...
域实体内部的构造和它真正强大的优势。我们可以将所有条件检查移入状态类并清除域实体类。
但是如何在不违反封装原则的情况下修改封装在域对象中的数据呢?
例如,考虑 Account
实体。简单地说,它有两种可能的状态- Active
以及 Blocked
存款和取款的方法。
在国家模式下,我们应该把存取款的责任交给国家阶级。这里是uml图。
但我们如何修改 money
以及 state
来自的字段 AccountState
实现?我只看到了一条路,在这条路上我有一个公共的设定者。但是它违反了封装原则。通过这种方法,我还可以将私有字段更改为公共字段。
代码示例:
class Account {
private int money;
private AccountState state;
public Account() {
this.money = 0;
this.state = new Active();
}
public void deposit(int amount) {
this.state.deposit(this, amount);
}
public void withdraw(int amount) {
this.state.withdraw(this, amount);
}
public int getMoney() {
return this.money;
}
public AccountState getState() {
return this.state;
}
}
interface AccountState {
public void deposit(Account account, int amount);
public void withdraw(Account account, int amount);
}
class Active implements AccountState {
public void deposit(Account account, int amount) {
// How to change account money and state without setters and public fields usage?
}
public void withdraw(Account account, int amount) {
if (account.getState() instanceof Blocked) {
throw new RuntimeException("Money could not be withdrawn. Account is blocked.");
}
if (account.getMoney() - amount <= 0) {
throw new RuntimeException("Money could not be withdrawn. Insufficient funds.");
}
// How to change account money and state without setters and public fields usage?
}
}
class Blocked implements AccountState {
public void deposit(Account account, int amount) {
// How to change account money and state without setters and public fields usage?
}
public void withdraw(Account account, int amount) {
if (account.getMoney() - amount <= 0) {
throw new RuntimeException("Money could not be withdrawn. Insufficient funds.");
}
// How to change account money and state without setters and public fields usage?
}
}
这是一个非常简单的例子,但它很好地反映了我的问题。不幸的是,我找不到一个好的解决办法。我看到的所有示例都使用公共setter或公共字段。另外,我还看到了 Refactoring to Patterns
约书亚·克里耶夫斯基的书。他提供将setter与包级别的访问一起使用(没有像这样的访问修饰符) private
, public
,或 protected
). 因此,我们可以从与域实体位于同一包中的状态类更改实体数据,而不能从其他包更改实体数据。但是这种方法是使用特定于语言的特性包级别的访问。在其他语言如php中,这是行不通的。我在寻找一个概念性的解决方案。
有人能给出一个解决这个问题的实际生产示例吗?我真的很感激。
4条答案
按热度按时间o4tp2gmn1#
我会:
移动
money
进入AccountState
(作为AccountState
主要与money
在本例中)提供一种操纵
Account
在某种程度上你开了处方。这可能是通过以下方法实现的Account#transact(String label, double amount)
,允许您在不暴露成员的情况下操纵平衡。删除accountstate作为冗余类,因为类的字段表示对象的状态。
第二种方法也可以通过函数api来实现,但不要将类成员的可变性与破坏封装混为一谈;封装的目的是禁止不需要的行为(如任意数学或访问内部集合)。这可以防止类进入错误状态。
jdg4fx2g2#
公共设置器(或实际上的设置器,通常不考虑访问修饰符)不会违反封装。封装意味着我们设置了类,这样只有类中带有变量的方法才能引用示例变量。在正确封装的类中,调用方因此需要使用这些方法来修改类字段。
0sgqnhkj3#
有很多方法可以解决这个问题,具体取决于您需要每个状态示例做什么。在这个特定的示例中,我将传递
money
进入AccountState
而不是整个Account
对象。下面是一个使用枚举的示例,但很明显,这可能是两个单独的类,而不是一个接口。
op中的代码很难应用oop模式的一个线索是,业务逻辑方法(
deposit
以及withdraw
)返回void
. 除了程序性编程之外,很难用void
方法。使您的方法返回适当的值,这样您就可以更轻松地组成自然交互的类。dffbzjpn4#
为了只允许来自特定类的调用,可以使用反射。
java示例:如何在java中获取caller类
php示例:https://stackoverflow.com/a/6927569/724099