1-й вариант: Не использовать изменяемый объект, а каждый раз, когда вы хотите поменять что-то, создавать новый объект, при этом, сам объект должен быть
immutable. Доступ к объекту осуществлять через фабрику. Такое решение имхо будит
наиболее правильным.
Пример:
immutable класс
public final class User {
private final String login;
private final Date dateCreate;
private final String description;
private final Set<String> roles;
public User(String login, Date dateCreate, String description, Set<String> roles) {
this.login = login;
this.dateCreate = (Date) dateCreate.clone();
this.description = description;
this.roles = Collections.unmodifiableSet(roles);
}
public String getLogin() {
return login;
}
public Date getDateCreate() {
return (Date) dateCreate.clone();
}
public String getDescription() {
return description;
}
public Set<String> getRoles() {
return roles;
}
}
Фабрика
public class UserFactory<T> {
private final AtomicReference<T> atomicReference;
public UserFactory(T object) {
this.atomicReference = new AtomicReference<>(object);
}
public T getObject() {
return atomicReference.get();
}
public void setObject(T object) {
atomicReference.set(object);
}
}
public static void main(String[] args) {
User user = new User("user1", new Date(), "test user", new HashSet<>(Arrays.asList("READ", "ADD")));
UserFactory<User> userFactory = new UserFactory<>(user);
exampleUsingUser(userFactory);
}
public static void exampleUsingUser(UserFactory<User> userFactory) {
User user = userFactory.getObject();
System.out.println(user.getLogin());
System.out.println(user.getDescription());
System.out.println(user.getRoles());
}
2-й вариант: эдакий велосипед:) Контролировать доступ через
прокси с использованием
ReentrantReadWriteLockПример:
Для начала нам нужно выделить интерфейс класса, состояние объекта которого будет меняться
public interface Settings {
String getColor();
void setColor(String color);
String getDescription();
void setDescription(String description);
int getSize();
void setSize(int size);
}
Реализуем интерфейс
public class SettingsImpl implements Settings {
private volatile String color;
private volatile String description;
private volatile int size;
public static Settings newSettings() {
return ProxySettingsKeeper.createProxy(new SettingsImpl());
}
private SettingsImpl() {
}
@Override
public String getColor() {
return color;
}
@Override
public void setColor(String color) {
this.color = color;
}
@Override
public String getDescription() {
return description;
}
@Override
public void setDescription(String description) {
this.description = description;
}
@Override
public int getSize() {
return size;
}
@Override
public void setSize(int size) {
this.size = size;
}
}
Прокси
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ProxySettingsKeeper implements InvocationHandler {
private final Settings settings;
private final ReentrantReadWriteLock.ReadLock readLock;
private final ReentrantReadWriteLock.WriteLock writeLock;
public ProxySettingsKeeper(Settings settings) {
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
this.readLock = readWriteLock.readLock();
this.writeLock = readWriteLock.writeLock();
this.settings = settings;
}
public static Settings createProxy(Settings settings) {
return (Settings) Proxy.newProxyInstance(settings.getClass().getClassLoader()
, new Class[]{Settings.class}
, new ProxySettingsKeeper(settings));
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
if (name.startsWith("get")) {
readLock.lock();
try {
return method.invoke(settings, args);
} finally {
readLock.unlock();
}
} else if (name.startsWith("set")) {
writeLock.lock();
try {
return method.invoke(settings, args);
} finally {
writeLock.unlock();
}
}
return method.invoke(settings, args);
}
}
public static void main(String[] args) {
Settings settings = SettingsImpl.newSettings();
settings.setDescription("test");
System.out.println(settings.getDescription());
}