I am creating a logic for web application to managing consents from user.
The model class that is persisted in the DB will have multiple fields, from which only a set will be changed with user request. E. g. class will have 10 fields with various consents, but user will be willing to change only 2 of those. To avoid writing a big chain of if-else's I designed this classes, to harness polymorphism to do the job for me, but somehow this design seems flawed to me. Could you tell me if this is proper way to do it?
PROBLEM: Change values of only subset of fields from large set of fields in class.
For sake of simplicity I removed getter/setters methods and some fields.
Main logic for changing consents:
public class GdprServiceImpl implements GdprService {
private final ConsentRepository consentRepository;
#Autowired
public GdprServiceImpl(ConsentRepository consentRepository) {
this.consentRepository = consentRepository;
}
#Override
public void changeConsent(User user, List<ConsentDto> consents) {
Optional<Consent> optionalConsent = consentRepository.findByUser(user);
if(optionalConsent.isPresent()) {
Consent consent = optionalConsent.get();
for(ConsentDto consentDto : consents) {
consentDto.apply(consent);
}
consentRepository.save(consent);
}
else {
Consent consent = new Consent();
consent.setUser(user);
for(ConsentDto consentDto : consents) {
consentDto.apply(consent);
}
consentRepository.save(consent);
}
}
Model class:
public class Consent {
private Boolean messageConsent;
private Boolean recordConsent;
/*CONSTRUCTOR, OTHER METHODS AND FIELDS OMITTED*/
}
Classes that will change a set of fields from Consent class:
public abstract class ConsentDto {
public abstract void apply(Consent consent);
}
public class RecordConsentDto extends ConsentDto {
private boolean consentValue;
public RecordConsentDto(boolean consentValue) {
this.consentValue = consentValue;
}
#Override
public void apply(Consent consent) {
consent.setRecordConsent(consentValue);
}
}
public class MessageConsentDto extends ConsentDto {
private boolean consentValue;
public MessageConsentDto(boolean consentValue) {
this.consentValue = consentValue;
}
#Override
public void apply(Consent consent) {
consent.setMessageConsent(this.consentValue);
}
}
You are right about the design having a "smell".
This is because the DB design is not normalized.
having a list of consents in one record is an indication. while technically it is allowed, classic RDBMS design dictatets that arrays should be represented as either one-to-many or many-to-many relation between tables. Of course, same in the object model.
a Fully normalized solution will have a consent_catalog table and many-to-many relation to users:
table consent_catalog {
int id // PK
String name
}
The catalog acts as "consent enum", having one row per type of consent (record, message, etc)
table user_consents {
int user_id references users(id)
int consent_id references consent_catalog(id)
}
This table has rows only for consents accepted by the user. no "false" consents. This design opens up new possibilities like knowing which users have a specific consent or mulitple consents in common.
This design feels like an overkill. At the end of the day you are always calling consent.setMessageConsent() or similar it's wrapped with an enum field and a class implementing ConsumerDto (which is really a Consumer). Generally DTO are not supposed to implement business logic yet one could argue that apply method is one.
It really would be cleaner to have UserConsent POJO with Boolean fields. The exception would be if triggering one consent should trigger other but it's not clear from your example.
Just my two cents. I'd prefer to see either an anemic POJO passed around or DDD aggregate root for user that manages consents but not something in between.
Related
I've read and came to realize myself that entities (data objects - for JPA or serialization) with injections in them is a bad idea. Here is my current design (all appropriate fields have getters and setter, and serialVersionUID which I drop for brevity).
This is the parent object which is the head of the entity composition graph. This is the object I serialize.
public class State implements Serializable {
List<AbstractCar> cars = new ArrayList<>();
List<AbstractPlane> planes = new ArrayList<>();
// other objects similar to AbstractPlane as shown below
}
AbstractPlane and its subclasses are just simple classes without injections:
public abstract class AbstractPlane implements Serializable {
long serialNumber;
}
public class PropellorPlane extends AbstractPlane {
int propellors;
}
public class EnginePlane extends AbstractPlane {
List<Engine> engines = new ArrayList<>(); // Engine is another pojo
}
// etc.
In contrast, each concrete type of car requires a manager that holds some behavior and also some specific form of data:
public abstract class AbstractCar implements Serializable {
long serialNumber;
abstract CarData getData();
abstract void operate(int condition);
abstract class CarData {
String type;
int year;
}
}
public class Car1 extends AbstractCar {
#Inject
Car1Manager manager;
Car1Data data = new Car1Data(); // (getter exists per superclass requirement)
void operate(int i) { // logic looks weird but makes the example
if (i < 0)
return manager.operate(data);
else if (i > 1)
return manager.operate(data, i);
}
class Car1Data extends CarData {
int property1;
{
type = "car1";
year = 1;
}
}
}
public class Car2 extends AbstractCar {
#Inject
Car2Manager manager;
Car2Data data = new Car2Data();
void operate(int i) {
if (i < 31)
return manager.operate(data);
}
class Car2Data extends CarData {
char property2;
{
type = "car2";
year = 12;
}
}
}
// etc.
The CarxManager are #Stateless beans which perform operations on the data (the matching CarxData) given to them. They themselves further use injections of many other beans and they are all subclasses of AbstractCarManager. There are O(100) car types and matching managers.
The issue when serializing the State is that serializing the list of abstract cars does not play well with the injections in the subclasses. I'm looking for a design that decouples the injection from the data saving process.
My previous related questions: How to serialize an injected bean? and How can I tell the CDI container to "activate" a bean?
You can use the repository pattern. Place your business logic into a service and inject the repository (which abstracts the persistence mechanism) and manager into that. The repository hides the persistence implementation details from the business service and the entities are just simple POJOs.
It would look something like the below with Foo being the id of the entity Bar:
public class CarService {
#Inject
CarRepository carRepository;
#Inject
CarManager manager;
piblic void operate(final Foo foo) {
Bar myBar = carRepository.retrieve(foo);
manager.doSomethingTo(myBar);
carRepository.persist(myBar);
}
}
See also: Repository Pattern Step by Step Explanation, http://deviq.com/repository-pattern/. Some frameworks such as Spring Data JPA or deltaspike already implement the repository pattern for you, all you need to do is provide an interface like the following and they generate the implementation in the background:
#Repository
public interface CarRepository extends EntityRepository<Car, UUID> {}
Mark in answer to your request for more detail I am going to provide a remodeled solution because the example in the question really did not make sense to me and exhibits quite a few anti-patterns which lead to problematic software.
To find a good solution to the problem touches on a lot of different considerations, many of which are very large topics with many books written about them, but I will try my best to illustrate my thinking based on these to solve the above problem.
And apologies as I have no doubt you are aware of many of these, but I shall assume limited knowledge for the sake of clarity.
The first step in solving this problem is not about code, but about the model itself, model driven development is covered extensively in Eric Evan's book as mentioned in the comments below. The model should drive the implementation and should also exist on its own tier as part of a layered architecture and is made up of entities, value objects and factories.
Model Driven Development
In the model given in the question we have something called a State, which contains AbstractPlanes and AbstractCars. You are using JPA to persists the State which is effectively an aggregate of your planes and cars. Firstly calling anything a State in software is a bad smell because pretty much everything has some sort of state, but calling what we have here which is an aggregate the State makes even less sense.
How does one State differ from another? Is one car part of one State and another part of a different State or is it the case that all planes and cars belong to a single instance of State. What is the relationship between planes and cars in this scenario? How does a list of planes and a list of cars have any relation to a single State entity?
Well if State was actually an Airport and we were interested in how many planes and cars were currently on the ground, then this could be the correct model. If State was an Airport it would have a name or identity such as its airport code, but it does not and so...
... in this case, it seems that State is an object which is being used as a convenience to allow us to access the object model. So we are effectively driving our model by implementation considerations, when we should doing it the other way round and driving our implementation from our model.
Terms like CarData are also problematic for the same reason, creating a Car entity and then a separate object to store its Data is messy and confusing.
Failure to get the model right results in software that is at best confused and at worst completely non-functional. This is one of the largest causes of failed IT programmes and the bigger the project the harder this stuff is to get right.
Revised Model
So from the model I understand that we have Cars and we have Planes, instances of which are all unique entities with their own identity. They seem to me to be separate things and so there is no point in persisting them wrapped in some aggregate entity.
public class Plane {...}
public class Car {...}
Another consideration is the use of abstract classes in the model, generally we want to apply the principle of favoring composition over inheritance because inheritance can result in hidden behaviors and it can make a model hard to read. For example why have we got a ProperllerPlane and an EnginePlane? Surely a propeller is just a type of engine? I have greatly simplified the model:
public class Plane implements Serializable {
#Id
private String name;
private String model;
private List<Engine> engines;
The Plane is an entity with its own attributes and identity. There is no need to have additional classes which represent nothing in the real world just to store attributes. The engine object is currently an enum representing the type of engine used in the plane:
public enum Engine {
PROPELLER, JET
}
If the engine itself were to require an identity, as in real life engine serial numbers and things are tracked, then we would change this to an object. But we might not want to allow access to it except through a Plane entity instance, in which case the Plane will be known as a aggregate root - this is an advanced topic and I would recommend Evan's book for more details on aggregates.
The same goes for the Car entity.
#Entity
public class Car implements Serializable{
#Id
private String registration;
private String type;
private int year;
The above is all you need from what was provided in the question for the basis of your model. I have then created a couple of factory classes which handle creation of instances of these entities:
public class CarFactory {
public Car makePosrche(final String registrationNumber) {
Car porsche = new Car();
porsche.setRegistration(registrationNumber);
porsche.setType("Posrshe");
porsche.setYear(1986);
return porsche;
}
}
public class PlaneFactory {
public Plane makeSevenFourSeven(final String name) {
Plane sevenFourSeven = new Plane();
List<Engine> engines = new ArrayList<Engine>();
engines.add(JET);
engines.add(JET);
engines.add(JET);
engines.add(JET);
sevenFourSeven.setEngines(engines);
sevenFourSeven.setName(name);
return sevenFourSeven;
}
public Plane makeSpitFire(final String name) {
Plane spitFire = new Plane();
List<Engine> engines = new ArrayList<Engine>();
engines.add(PROPELLER);
spitFire.setEngines(engines);
spitFire.setModel("Spitfire");
spitFire.setName(name);
return spitFire;
}
}
What we are also doing here is separating out concerns as according to the Single Responsibility Principle each class should only really do one thing.
Now that we have a model we need to know how to interact with it. In this case we would most likely if using JPA persist the Cars in a table called Car and the Planes likewise. We would provide access to these persisted entities via repositories, CarRepository and PlaneRespository.
You can then create classes called services which inject the repositories (and anything else you require) to perform CRUD (Create Read Update Delete) operations on the instances of cars and planes and also this is the point where you can apply your business logic to these. Such as your method:
void operate(int i) {..}
By structuring your code this way you decouple the model (entities and value objects) from how they are persisted (repositories) from the services which operate on them as mentioned in your question:
I'm looking for a design that decouples the injection from the data saving process.
A possibility is to remove the property, so it won't be picked up by the serializers. This can be achieved be getting it programmatically.
private Car2Manager getCar2Manager() {
CDI.current().select(Car2Manager.class).get();
}
I would not consider this a clean solution, but it should be a workable "solution"
Also which might work is using JPA's #Transient:
#Inject
#Transient
Car2Manager manager;
I have not tested this, so it might not work.
What is the entry point?
Is this a web application, a rest service, a soap service, or event a scheduler?
Injection frameworks almost always separate data and service. Data are always POJO, containing absolutely no business logic. Here, assuming this is a rest-service, i will do the following:
public class SSOApplication {
public class State implements Serializable {
List<AbstractCar> cars = new ArrayList<>();
List<AbstractPlane> planes = new ArrayList<>();
// other objects similar to AbstractPlane as shown below
}
public abstract class AbstractPlane implements Serializable {
long serialNumber;
}
public class PropellorPlane extends AbstractPlane {
int propellors;
}
public class EnginePlane extends AbstractPlane {
List<Engine> engines = new ArrayList<>(); // Engine is another pojo
}
public abstract class AbstractCar implements Serializable {
long serialNumber;
abstract CarData getData();
}
public static class CarData {
String type;
int year;
}
public class Car2Data extends CarData {
char property2;
{
type = "car2";
year = 12;
}
}
public static class Car1Data extends CarData {
int property1;
{
type = "car1";
year = 1;
}
}
public static class Car1 extends AbstractCar {
#Override
CarData getData() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}
public static class Car2 extends AbstractCar {
#Override
CarData getData() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}
public static interface CarManager<T extends CarData> {
void operate(T car, int index);
default boolean canHandle(T carData) {
final TypeToken<T> token = new TypeToken<T>(getClass()) {
};
return token.getType() == carData.getClass();
}
}
#ApplicationScoped
public static class Car1Manager implements CarManager<Car1Data> {
public void operate(Car1Data car, int index) {
}
}
#ApplicationScoped
public static class Car2Manager implements CarManager<Car2Data> {
public void operate(Car2Data car, int index) {
}
}
#ApplicationScoped
public static class CarService {
#Any
#Inject
private Instance<CarManager<?>> carManagers;
public void operate(int index, AbstractCar car) {
final CarData carData = car.getData();
final CarManager<?> carManager = carManagers.stream()
.filter((mng) -> mng.canHandle(carData))
.findFirst()
.orElse(IllegalArgumentException::new);
carManager.operate(carData, index);
}
}
}
If you could alter your flow than perhaps you could do something like this:
class Car1InnerService {
#Inject
Car1Manager manager;
void operate(int i, Car1 car) {
if (i < 0)
return manager.operate(car.getData());
else if (i > 1)
return manager.operate(car.getData(), i);
}
}
}
I introduced some inner service which will operate on Car1 and use Car1Manager for it. Your AbstractCar class will also of course lose it's operate method because from now on your service will handle it. So now instead of calling car1.operate(i) you will have to make a call via Service like this:
public class SampleCar1ServiceUsage{
#Inject
Car1InnerService car1InnerService;
public void carManipulator(List<Car1> carlist){
int i = 0; //I don't know why you need this param therefore i just increment it
for(Car1 car: carlist){
car1InnerService.operate(i, car);
i++;
}
}
}
Of course you should introduce similar functionality for every other AbsractCar children (perhaps even extract some abstraction if necessary like for example AbsractCarInnerService which would define operate method or some interface which would do the same if you don't want any other solid methods in it). However this answer is still somehow related to #Justin Cooke answer and in my opinion you should definitely check those patterns which he mentioned in his post.
If we have many to many relationship we break them in to 1-m by introducing a reference table and that should be depict in the ER diagram.
For example if Item and Order is m-m we introduced ItemOrder reference table.
But while developing the system, do we identify ItemOrder as a entity (a domain class) ?
Yes, no and maybe.
Would you have an ItemOrder class? Maybe!
Would it be a "domain class"? No it would not. The keyword in domain class is domain. What constitutes a domain class depends on how your stakeholders perceive the domain. In this case, they probably have never thought of an "Item Order" - there's just an Order and it has a relationship with Items. "Order" and "Item" are relevant domain concepts because they are familiar to your stakeholder; "Item Order" is not.
But you might have an ItemOrder class anyways - but it's probably internal. For example, if you're using Row Data Gateway objects your Order class might look like this:
public class OrderRDG {
private UUID orderId;
public static OrderRDG[] find() { /* SQL code etc... */ }
public static OrderRDG[] findById() { /* SQL code etc... */ }
public UUID getOrderId() { return orderId; }
public Item getItems() {
ItemRDG[] items = []; // yes, I know Java arrays don't work like this - it's only an example
OrderItemRDG[] orderItems = OrderItemRDG.findItemsByOrderId(orderId);
for(OrderItemRDG orderItem:orderItems) {
ItemRDG item = ItemRDG.findById(orderItem.getItemId());
items.push(item);
}
return items;
}
}
Some container class holds list of objects of base class "Table" (like WoodenTable, MetalTable...). Each Table class keeps its MaterialType (MaterialType.Wood, MaterialType.Metal...). Question is how to provide proper getter method for container class that could return each SubClass of Table.
So far I've found following ways:
1.Getter with material type as parameter. Danger here is ClassCastException if type of T doesn't correspond to materialType:
<T extends Table> T getTable(MaterialType materialtype)
WoodenTable table = getTable(MaterialType.Wood);
MetalTable table = getTable(MaterialType.Wood); // ups... exception
2.Getter with Class parameter. Safe but not so clear for user (comparing to MaterialType as parameter):
<T extends Table> T getTable(Class<T> tableClass)
WoodenTable table = getTable(WoodenTable.class);
3.Getter for each Table SubClass. Cumbersome to use,write and add new Table subClasses:
WoodenTable getWoodenTable()
WoodenTable table = getWoodenTable();
4.Getter for just Table interface. Cast done outside of container class if necessary.
Table getTable(MaterialType materialType)
WoodenTable woodenTable = (WoodenTable)getTable(MaterialType.Wood)
Is any other (better) way to do that? If not, then which of those would be most appriopriate or least smelly?
It should be just this simple:
public Table getTable()
{
return this.table;
}
This will return a Table object it is up to the invoker to do with it what they want. Similar to the following block:
public Collection<String> getCollection()
{
return new ArrayList<String>();
}
The body returns an ArrayList but the function really returns a Collection. A well defined API that utilizes the most common interface between objects will give you this same flexibility.
EDIT
Yes, that could be one use case but quite often I need also something
like : for(Table t : TableContainer) {
(SubClassA)t.doSomethingSpecificForA();} and that's were my problems
begin.
Let us assume the following interface and implementations:
public interface Table
{
Table getTable();
void doSpecial();
}
public class WoddenTable implements Table
{
...
public Table getTable()
{
return this;
}
public void doSpecial()
{
mySpecial();
}
private void mySpecial()
{
System.out.println("Wooden");
}
}
public class MetalTable implements Table
{
...
public Table getTable()
{
return this;
}
public void doSpecial()
{
mySpecial();
}
private void mySpecial()
{
System.out.println("Metal");
}
}
and the following code:
public static void main(String[] args)
{
Collection<Table> tables = new ArrayList<Table>();
tables.add(new WoodenTable());
tables.add(new MetalTable());
for(Table table : tables)
{
table.doSpecial();
}
}
The approach here is that there is a shared public API. And the internals of each class are not exposed so the need to do something special for each class is hidden behind the common interface. This prevents having to do instance of checks or any of the other messy ways to solve this type of problem.
I would recommend to stop thinking about tables as about data structures with attributes (material), and start treating them as "persons" (Object Thinking). Don't get a "material" out of them. Instead, let them expose their behavior.
When you change the design of the table you will automatically change the design of their container. And it will become obvious that the container shouldn't care about tables' materials, but should let them control whether they want to get out of container or remain there.
I have the following problem illustrated by the pseudo code below (might not make a whole lot of sense):
class Form {
boolean loggedOn = false;
String id = null;
...get/set shared methods
}
class SearchFormA extends Form{
String name = null;
String email = null;
...get/set methods
}
class SearchFormB extends Form{
String age = null;
String gender = null;
...get/set methods
}
class Search {
public Search(HttpServletRequest request){
String searchMode = (String) request.getSearchMode();
if("0".equals(searchMode)){
SearchFormA formA = new SearchFormA();
formA.setName((String)request.getParameter("name"));
formA.setId((String)request.getParameter("id"));
...populate form
request.getSession().setAttribute("formA",formA);
}
if("1".equals(searchMode)){
SearchFormB formB = new SearchFormB();
formB.setAge((String)request.getParameter("age"));
formB.setId((String)request.getParameter("id"));
...populate form
request.getSession().setAttribute("formB",formB);
}
...rest of code
}
}
What i've done is using reflection, but is there any other way to do it at compile time?
I've also tried Factory method, but the classes SearchFormA and SearchFormB does not have much in common.
EDIT: ok basically, in this case i have 4-5 searchModes, each searchMode has a different form. Between these forms they share certain similar fields. In future i might have to add 10 searchModes and it's going to be repeating alot of populating of the same fields.
Unfortunately your code is too much "pseudo." For example it is not clear where do you take searchMode from. Also initialization of your specific forms is hard coded. I believe that in real life all these data is taken from class Data.
So, I assume that class Data contains name, age, id. Now the question is how does Data class contain this data. Special fields? Generic hash table?
In any case I think that the fact that you have some kind of "general" code (Search class) and have to create special forms is a bad pattern. You should use some MVC framework that populates your specific form automatically. Then run your search logic using the form data. if you have shared logic for both modes implement it either in abstract class or in utility class.
I don't see much wrong with your current approach. You could use an enum to avoid the string comparisons as in the following example:
public enum SearchMode {
A {
#Override
public Form createForm(Data data) {
SearchFormA form = new SearchFormA();
form.setName(...);
populateCommon(form, data);
return form;
}
},
B {
#Override
public Form createForm(Data data) {
SearchFormB form = new SearchFormB();
form.setAge(...);
populateCommon(form, data);
return form;
}
};
public abstract Form createForm(Data data);
public void populateCommon(Form form, Data data) {
// Set common properties
form.setId(...);
}
public static Form createForm(String searchMode, Data data) {
return SearchMode.valueOf(searchMode).createForm(data);
}
}
What you're obviously trying to avoid is that in the Search method you'd have to keep appending to the if structure (spaghetti code).
I feel like the situation you're describing isn't all that different from the one I wrote this answer for. That would mean that Strategy and Template (and others) could come in handy here.
Cheers,
Wim
This question already has answers here:
Closed 12 years ago.
Possible Duplicates:
‘Friends’ equivalent for Java?
Is there a way to simulate the C++ ‘friend’ concept in Java?
In C++ there is a concept of a "friend", which has access to a class's private variables and functions. So if you have:
class Foo {
friend class Bar;
private:
int x;
}
then any instance of the class Bar can modify any Foo instance's x member, despite it being private, because Bar is a friend of Foo.
Now I have a situation in Java where this functionality would come in handy, but of course it doesn't exist in Java.
There are three classes: Database, Modifier, Viewer. The Database is just a collection of variables (like a struct). Modifier should be "friends" with Database; that is, it should be able to read and write its variables directly. But Viewer should only be able to read Database's variables.
How is this best implemented? Is there a good way to enforce Viewer's read-only access of Database?
There are varying opinions on how pure you should be, but to me you shouldn't give anyone access to any fields. Use getters and setters.
If you need to segregate who can read and who can write, you can throw interfaces into the mix. Define an interface with just the getters and you can restrict your viewer to read only.
Robert Harvey added a comment that points to other options, like using a different access modifier for class or package level access.
I don't think this is a good way to implement such a thing.
You should have a data access layer that hides the database. It should expose CRUD operations.
Start with interfaces. Create a ReaderDao that only has finder methods. Then create a GenericDao that extends the ReaderDao and adds the save, insert, and delete methods.
Implementation classes should implement one interface or the other as needed.
Neither one needs to expose the fact that it's a relational database behind the scenes.
An inner class has access to all the private members of its container class. Make your getters public and your setters private, make Modifer an inner class of Database. Also make it only creatable from inside of Database through a Factory Method pattern. Probably needs to be a Singleton as well.
A package local class is about as close to a "friend" in C++. Make all your getters public, make your setters package local. Make Modifier be in the same package as Database. The first example is cleaner.
The other idiom is applicable is the Memento pattern.
Are you sure this is a good pattern? It directly circumvents encapsulation, one of the main tennets of OO design.
You could create a nested class.
Imagine you have the ViewClass like this:
class Viewer {
Database db;
Viewer( Database db ){
this.db = db;
}
public void whatIsX(){
System.out.println( db.x() );
}
}
And define a modifier with a method to modify that database.
abstract class Modifier {
public void manipulate();
}
You can create the Database with an nested class which have access to the private member, pretty much like friend.
class Database {
private int x;
class DatabaseModifier extends Modifier {
public void manipulate(){
x++;
}
}
public int x(){
return x;
}
}
// To see if works:
class Main{
public static void main( String [] args ) {
Database database = new Database();
Modifier modifier = database.new DatabaseModifier();
Viewer viewer = new Viewer( database );
viewer.whatIsX();
modifier.manipulate();
viewer.whatIsX();
}
}
You can also opt for a static inner class. It could be like this:
class Viewer {
Database db;
public Viewer( Database db ){
this.db = db;
}
public void whatIsX(){
System.out.println( db.x() );
}
}
class Modifier {
Database db;
public Modifier( Database db ){
this.db = db;
}
public void manipulate(){
//db.x++; doesn't work because db.x is private
}
}
class Database {
private int x;
static class DatabaseModifier extends Modifier {
public DatabaseModifier( Database db ){
super(db);
}
#Override
public void manipulate(){
db.x++;
}
}
// public accessor to attribute X
public int x(){// should be getX()
return x;
}
}
class Main{
public static void main( String [] args ) {
Database database = new Database();
Modifier modifier = new Database.DatabaseModifier( database );
Viewer viewer = new Viewer( database );
viewer.whatIsX();
modifier.manipulate();
viewer.whatIsX();
}
}
If you want this kind of access, create an interface to the class that encapsulates the methods you intend to allow only a select few to call. Then create a method where the class passes a private instance of the interface only if the requesting class meets whatever criteria you impose.
A simple example, written with tongue firmly in cheek:
public class Husband {
private Spouse wife;
private int cashInWallet;
public Husband(Wife wife) {
this.cashInWallet = 20;
this.wife = wife;
}
public WalletAccess getWalletAccess(Object other) {
if (other instanceof Wife) {
return new WalletAccessImpl(this);
}
return null;
}
public interface WalletAccess {
public int withdrawCash(int requested);
}
private WalletAccessImpl implements WalletAccess {
private Husband hubby;
private WalletAccessImpl(Husband hubby) {
this.hubby = hubby;
}
public int withdrawCach(int requested) {
if (this.hubby.wallet > requested) {
this.hubby.wallet -= requested;
return requested;
} else {
int allCash = this.hubby.wallet;
this.hubby.wallet = 0;
return allCash;
}
}
}
public class Wife {
private Husband husband;
public Wife(Husband husband) {
this.husband = husband;
}
public void consumeCash() {
Husband.WalletAccess access = husband.getWalletAccess(this);
int cash = access.withdrawCash(20);
}
}
More complex examples are possible, but this improves on the friend access pattern, as even individual instances of the "would-be" friend class could be singled out for access (or refusal). For example, it would be trivial to check that the Wife was actually married to this particular husband in this case by rewriting getWalletAccess(...) like so:
public WalletAccess getWalletAccess(Object other) {
if (other instanceof Wife) {
Wife someone = (Wife)other;
if (someone.getHusband() == this) {
return new WalletAccessImpl(this);
}
}
return null;
}
I'd say interfaces are definitely the solution for your problem.
To start with, don't allow anything else to have access to the actual fields of Database. Create methods that each return one of its fields. Then, make DatabaseView an interface that declares each of these methods for reading. Then have Database implement DatabaseView. Finally, you can add methods for writing (setting) the fields that should be settable to Database.
When you have some class that needs to be able to read from the database, such as your Viewer, have it take a DatabaseView. You can then pass in the Database instance itself, but that class will not know about the methods for writing to it. If something else needs to both read and write, such as your Modifier, you can just give it the Database itself. Better yet, Database itself could be an interface that extends DatabaseView and your actual implementation object could be some class that nothing needs to know about.
You should rethink how much seperation you really want, this "Modifier should be "friends" with Database; that is, it should be able to read and write its variables directly." is impossible in Java - there can't be different access privileges for direct field accesses.
I assume what you really want is to strongly discourage undesirable access patterns. There are a few ways how this can be achieved:
1.) Put Modifier and Database in the same package and make the "Setters" package protected, thus the set-methods become invisible to your Viewer (as long as Viewer is not in the same package). This is more or less impractical for larger designs.
2.) Seperate the concerns into completely different Projects. Then you can set the projects dependencies that only Modifier gets access to Database at all. This implies that you change your design somewhat, either Database becomes two projects (one with the public-readonly interface and one with the full-access interface), or remove the dependency between Viewer and Database completely and make Viewer access Database only through Modifier. This solution has the advantage that its physically impossible to violate access boundaries (It will not compile in the build).
3.) A solution closer to the actual "friend" class concept would be to have two interfaces, one for read, one for write and Database implements those interfaces using inner classes. Then you can "guard" access to the inner class instances with getters that take the client as an argument like this:
public class Database {
public DatabaseReadAccess getReadAccess(Viewer viewer) { ... }
public DatabaseWriteAccess getWriteAccess(Modifier modifier) { ... }
}
This will not prevent a malicious access, but somewhat discourage them. If you want to go one step further, define "Token" classes for Viewer and Modifier and require the token instance for the access getters in Database (then the compiler will enforce the restrictions):
public class ModifierToken {
ModifierToken(Modifier modifier) {
// constructor is package protected, so no outsiders can create tokens!
}
}
I personally would go with the "separate projects" approach, it makes undesirable accesses obvious and violations pop up at latest in the build. I've never tried the "token" approach myself.