java—从状态类到上下文类的私有字段的访问

vpfxa7rd  于 2021-07-06  发布在  Java
关注(0)|答案(4)|浏览(344)

我对状态模式实现感到困惑。在这种模式下,我们应该将状态管理提取到单独的类中。乍一看,它让我们避免了大麻烦 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中,这是行不通的。我在寻找一个概念性的解决方案。
有人能给出一个解决这个问题的实际生产示例吗?我真的很感激。

o4tp2gmn

o4tp2gmn1#

我会:
移动 money 进入 AccountState (作为 AccountState 主要与 money 在本例中)
提供一种操纵 Account 在某种程度上你开了处方。这可能是通过以下方法实现的 Account#transact(String label, double amount) ,允许您在不暴露成员的情况下操纵平衡。
删除accountstate作为冗余类,因为类的字段表示对象的状态。
第二种方法也可以通过函数api来实现,但不要将类成员的可变性与破坏封装混为一谈;封装的目的是禁止不需要的行为(如任意数学或访问内部集合)。这可以防止类进入错误状态。

jdg4fx2g

jdg4fx2g2#

公共设置器(或实际上的设置器,通常不考虑访问修饰符)不会违反封装。封装意味着我们设置了类,这样只有类中带有变量的方法才能引用示例变量。在正确封装的类中,调用方因此需要使用这些方法来修改类字段。

0sgqnhkj

0sgqnhkj3#

有很多方法可以解决这个问题,具体取决于您需要每个状态示例做什么。在这个特定的示例中,我将传递 money 进入 AccountState 而不是整个 Account 对象。
下面是一个使用枚举的示例,但很明显,这可能是两个单独的类,而不是一个接口。

public class Account {
    private int balance = 0;
    private AccountState currentState = AccountState.ACTIVE;

    public int deposit(int amount) {
        balance = currentState.deposit(balance, amount);
        return balance;
    }

    public int withdraw(int amount) {
        balance = currentState.withdraw(balance, amount);
        return balance;
    }

    public AccountState activate() {
        this.currentState = AccountState.ACTIVE;
        return currentState;
    }

    public AccountState block() {
        this.currentState = AccountState.BLOCKED;
        return currentState;
    }

    enum AccountState {
        ACTIVE {
            @Override int deposit(int balance, int amount) {
                return balance + amount;
            }
            @Override int withdraw(int balance, int amount) {
                int newBalance = balance - amount;
                if (newBalance >= 0) {
                    return newBalance;
                }
                throw new IllegalArgumentException("Withdrawal amount is greater than balance.");
            }
        },
        BLOCKED {
            @Override int deposit(int balance, int amount) {
                throw new UnsupportedOperationException("Account is blocked.");
            }
            @Override int withdraw(int balance, int amount) {
                throw new UnsupportedOperationException("Account is blocked.");
            }
        };
        abstract int deposit(int balance, int amount);
        abstract int withdraw(int balance, int amount);
    }
}

op中的代码很难应用oop模式的一个线索是,业务逻辑方法( deposit 以及 withdraw )返回 void . 除了程序性编程之外,很难用 void 方法。使您的方法返回适当的值,这样您就可以更轻松地组成自然交互的类。

dffbzjpn

dffbzjpn4#

为了只允许来自特定类的调用,可以使用反射。
java示例:如何在java中获取caller类
php示例:https://stackoverflow.com/a/6927569/724099

相关问题