Java 中状态模式中的损坏封装
Broken encapsulation in State pattern in Java
我已经在 Java 中为我的套接字库实现了状态模式。
我简化代码以减小大小并提高可读性只是为了演示问题。
我不知道如何封装状态 classes 对 Socket class 的使用。
我写了评论以更好地理解问题。
所以,有一个示例代码:
package com.test;
enum ConnectionAttemptResult {
ConnectionAttemptStarted,
AlreadyInProgress,
AlreadyConnected,
UnableToCreateSocket,
UnableToConnectSocket,
UnableToCreateReadEvent
}
class SocketAddress {
static SocketAddress LocalhostPort(int port) {
// Code removed.
return new SocketAddress();
}
}
class Socket {
private State currentState;
Socket() {
this.currentState = new NotActiveState(this);
}
public ConnectionAttemptResult Connect(SocketAddress remoteAddress) {
return this.currentState.Connect(remoteAddress);
}
// Socket class is external and will be used by user.
// So further methods must be private because they are internal.
// Calling these methods by a user may broke class functionality.
// But I can't make them private because then states couldn't access them.
public void SwitchState(State newState) {
this.currentState = newState;
}
// I removed a lot of code of this class to simplify the code.
public boolean CreateSocket() {
// Code removed.
return true;
}
public boolean ConnectSocket(SocketAddress remoteAddress) {
// Code removed.
return true;
}
public boolean CreateReadEvent() {
// Code removed.
return true;
}
}
abstract class State {
protected Socket socket;
State(Socket socket) {
this.socket = socket;
}
public abstract ConnectionAttemptResult Connect(SocketAddress remoteAddress);
}
class NotActiveState extends State {
NotActiveState(Socket socket) {
super(socket);
}
@Override
public ConnectionAttemptResult Connect(SocketAddress remoteAddress) {
if (!socket.CreateSocket())
return ConnectionAttemptResult.UnableToCreateSocket;
if (!socket.ConnectSocket(remoteAddress))
return ConnectionAttemptResult.UnableToConnectSocket;
if (!socket.CreateReadEvent())
return ConnectionAttemptResult.UnableToCreateReadEvent;
socket.SwitchState(new ConnectingState(socket));
return ConnectionAttemptResult.ConnectionAttemptStarted;
}
}
class ConnectingState extends State {
ConnectingState(Socket socket) {
super(socket);
}
@Override
public ConnectionAttemptResult Connect(SocketAddress remoteAddress) {
return ConnectionAttemptResult.AlreadyInProgress;
}
}
class ConnectedState extends State {
ConnectedState(Socket socket) {
super(socket);
}
@Override
public ConnectionAttemptResult Connect(SocketAddress remoteAddress) {
return ConnectionAttemptResult.AlreadyConnected;
}
}
class Main {
public static void main(String[] args) {
Socket socket = new Socket();
socket.Connect(SocketAddress.LocalhostPort(9898));
// But I also can call internal methods and break the class functionality, because now they are public.
socket.CreateSocket();
socket.CreateReadEvent();
}
}
我的错误在哪里?
开发人员通常如何实现这种模式保存封装?
希望得到您的帮助!
提前致谢!
好吧,你可以让状态 classes 嵌套私有 classes 并且它们可以访问包含 class 的内部结构。正如我们所见,TransitiveState
可以访问 SomethingWithState#previousState
私有变量。我认为静态嵌套 classes 也可以访问封闭 class 的私有成员,但您必须将 SomethingWithState
实例传递给它们。
状态是一个实现细节,因此它们不会也不应该对外公开。
例如
public class StatePatternExample {
public static void main(String[] args) {
SomethingWithState something = new SomethingWithState();
something.next();
something.next();
}
private static class SomethingWithState {
private State state;
private State previousState;
SomethingWithState() {
this.previousState = null;
this.state = new InitialState();
}
void next() {
State next = state.next();
System.out.println(state.name() + " --> " + next.name());
previousState = state;
state = next;
}
private interface State {
State next();
String name();
}
private class InitialState implements State {
public State next() {
return new TransitiveState();
}
public String name() { return "Initial"; }
}
private class TransitiveState implements State {
public State next() {
System.out.println("Access private previousState from TransitiveState: was " + previousState.name());
return new FinalState();
}
public String name() { return "Transitive"; }
}
private class FinalState implements State {
public State next() {
throw new IllegalStateException("Already in final state.");
}
public String name() { return "Final"; }
}
}
}
我已经在 Java 中为我的套接字库实现了状态模式。 我简化代码以减小大小并提高可读性只是为了演示问题。 我不知道如何封装状态 classes 对 Socket class 的使用。 我写了评论以更好地理解问题。 所以,有一个示例代码:
package com.test;
enum ConnectionAttemptResult {
ConnectionAttemptStarted,
AlreadyInProgress,
AlreadyConnected,
UnableToCreateSocket,
UnableToConnectSocket,
UnableToCreateReadEvent
}
class SocketAddress {
static SocketAddress LocalhostPort(int port) {
// Code removed.
return new SocketAddress();
}
}
class Socket {
private State currentState;
Socket() {
this.currentState = new NotActiveState(this);
}
public ConnectionAttemptResult Connect(SocketAddress remoteAddress) {
return this.currentState.Connect(remoteAddress);
}
// Socket class is external and will be used by user.
// So further methods must be private because they are internal.
// Calling these methods by a user may broke class functionality.
// But I can't make them private because then states couldn't access them.
public void SwitchState(State newState) {
this.currentState = newState;
}
// I removed a lot of code of this class to simplify the code.
public boolean CreateSocket() {
// Code removed.
return true;
}
public boolean ConnectSocket(SocketAddress remoteAddress) {
// Code removed.
return true;
}
public boolean CreateReadEvent() {
// Code removed.
return true;
}
}
abstract class State {
protected Socket socket;
State(Socket socket) {
this.socket = socket;
}
public abstract ConnectionAttemptResult Connect(SocketAddress remoteAddress);
}
class NotActiveState extends State {
NotActiveState(Socket socket) {
super(socket);
}
@Override
public ConnectionAttemptResult Connect(SocketAddress remoteAddress) {
if (!socket.CreateSocket())
return ConnectionAttemptResult.UnableToCreateSocket;
if (!socket.ConnectSocket(remoteAddress))
return ConnectionAttemptResult.UnableToConnectSocket;
if (!socket.CreateReadEvent())
return ConnectionAttemptResult.UnableToCreateReadEvent;
socket.SwitchState(new ConnectingState(socket));
return ConnectionAttemptResult.ConnectionAttemptStarted;
}
}
class ConnectingState extends State {
ConnectingState(Socket socket) {
super(socket);
}
@Override
public ConnectionAttemptResult Connect(SocketAddress remoteAddress) {
return ConnectionAttemptResult.AlreadyInProgress;
}
}
class ConnectedState extends State {
ConnectedState(Socket socket) {
super(socket);
}
@Override
public ConnectionAttemptResult Connect(SocketAddress remoteAddress) {
return ConnectionAttemptResult.AlreadyConnected;
}
}
class Main {
public static void main(String[] args) {
Socket socket = new Socket();
socket.Connect(SocketAddress.LocalhostPort(9898));
// But I also can call internal methods and break the class functionality, because now they are public.
socket.CreateSocket();
socket.CreateReadEvent();
}
}
我的错误在哪里? 开发人员通常如何实现这种模式保存封装? 希望得到您的帮助! 提前致谢!
好吧,你可以让状态 classes 嵌套私有 classes 并且它们可以访问包含 class 的内部结构。正如我们所见,TransitiveState
可以访问 SomethingWithState#previousState
私有变量。我认为静态嵌套 classes 也可以访问封闭 class 的私有成员,但您必须将 SomethingWithState
实例传递给它们。
状态是一个实现细节,因此它们不会也不应该对外公开。
例如
public class StatePatternExample {
public static void main(String[] args) {
SomethingWithState something = new SomethingWithState();
something.next();
something.next();
}
private static class SomethingWithState {
private State state;
private State previousState;
SomethingWithState() {
this.previousState = null;
this.state = new InitialState();
}
void next() {
State next = state.next();
System.out.println(state.name() + " --> " + next.name());
previousState = state;
state = next;
}
private interface State {
State next();
String name();
}
private class InitialState implements State {
public State next() {
return new TransitiveState();
}
public String name() { return "Initial"; }
}
private class TransitiveState implements State {
public State next() {
System.out.println("Access private previousState from TransitiveState: was " + previousState.name());
return new FinalState();
}
public String name() { return "Transitive"; }
}
private class FinalState implements State {
public State next() {
throw new IllegalStateException("Already in final state.");
}
public String name() { return "Final"; }
}
}
}