I have to create a program that simulates Logic Operations (AND, OR, XOR, etc.) that is based on a Grid (My idea was to use an array). There are Logic Blocks that I have to construct as objects, which have to be placed onto the Grid to then Simulate the outcome. Each Block has connections to other blocks once placed on the grid, depending on how they are situated. These connections are defined for each block in its object definition. Some blocks have one input on the left side, and an output on the right side. Other blocks have one input on the left, and only outputs on all other sides, etc. Another attribute of the Blocks is their Orientation which is defined by 0, 1, 2, 3. 0 is the natural state of the blocks, 1 is rotated 90 degrees left, 2 is rotated 180 degrees to the left and so on. When they are rotated the connections obviously have to follow suit.
So far, based on the Blocks I have to create, I created an abstract parent class that incorporates the attributes that ALL Blocks have, such as the state of their connections (either 0, 1, or "open". I was thinking of using 9 for "open" to be able to use int datatype). Then I created another layer of 4 different classes that separate the blocks further into transport blocks (only transport the condition of the input to all outputs), modifier blocks (modify inputs), Constant blocks (Blocks that are always either 0 or 1), and InOut Blocks (an Input Block, the start of simulation path, and an Output Block, the end of the simulation path).
Why so many layers of classes? Its required in the assignment.
Is my thinking a good start for this project?
And what could a viable concept for the Rotation of the blocks be?
I wouldn't use ints to represent the rotation. Use an enum, maybe with values such as ROTATE_0, ROTATE_90, ROTATE_180 and ROTATE_270. Give them rotateClockwise() and rotateCounterClockwise() methods that return the appropriate value for a specific constant. You can also add an enum Side with values LEFT, RIGHT, TOP, BOTTOM, which you can use to query a Gate for its in- and output pins. The result is obviously dependent on the type of gate, and the rotation of a gate. Make use of the Optional class to indicate that a pin is present on a specific side.
The class EnumRotation has no use. Use Rotation directly. I don't know what Ober represents.
The fact that you have to implement some interface, doesn't mean you can't use an internal representation that makes more sense. You can just adapt the internals to the interface whenever needed. For now, forget the interface, and just focus on how you would implement a grid like this.
You can then implement different kinds of cells, and wrap them in RotatedCell to rotate them correctly. This is how an AND-gate could look like:
No. It simply checks whether a single cell has a connector or a given side. The responsibility of checking for connections between two cells lies with the Grid class.
You shouldn't use names like Ober, because they only express structural relationships, and have no actual semantic value. Organism is a super type of Animal, but there is no value in naming it SuperType.
Don't declare three variables all on the same line. You can declare them as three lines. You didn't show your Side class so I don't know the details.… only your spelling will have to be better than mine.
There is something very wrong with methods which set something on a SideHow do you know the Side reference passed will point to a Side in this Cell object? The following code definitely requires that:-
You need a way of remembering what signal was set for a given input pin. This is what the map is for. Here's an implementation to give you an idea:
At some point you'll find that many gates have a lot in common, so you may make a base AbstractGate class that implements all the Cell methods, and has an abstract Map<Side, Signal> applyLogic(Map<Side, Signal> inputs) method that converts input signals to output signals.
The Side enum is simple and only contains constants for LEFT, RIGHT, TOP and BOTTOM. The Signal enum contains LOW and HIGH, and some logical operators like not(), and(Signal), or(Signal), xor(Signal), etc... If you want, you can use a boolean instead of Signal, but I think Signal is more expressive. It's probably easier to use if your Signal enum has a way to convert between the two types: boolean toBoolean() and static Signal forBoolean(boolean).
I used an interface for Cell, because it makes your application flexible. When you write an abstract base class, you should almost always write an interface it implements, and use the interface in your code wherever you can. For instance, most of the Cells will be some implementation of AbstractGate, but the Grid should contain a Cell field, because you never know if you want to implement your Cell in a completely different way than the AbstractGate class does.
Well, I agree with what you wrote (and I'll take the part about having fields on separate lines to heart), but in this case the setSignal() method is more similar to a mapping operation, where the Side is a simple key. This is not a requirement, but my own design. If instead of Side, a Cell would have stateful Pins, I agree that a method setSignal(Pin pin, Signal sig) would be horrendous.
My idea was to make that the responsibility of the Grid. Side can have an opposite() method, and when the Grid wants to check if two Cells connect, it matches side x with side x.opposite() of the cell on side x.
Each connection (Input/output) can have 3 states: 0, 1, and open.
Connections are open when they have not yet been assigned either the 0 or 1 value.
Does it then make sense to map all the sides to open in the beginning?
And then have the methods run over the cell to map the correct values?
Note that your methods are not static, so your logic can be directly applied to 'this' enum. They also seem needlessly complex. Just compare the current enum to the argument. You can also use boolean operators directly if you associate Signals with a boolean value:
The AndGate is just a loose component. It's like a chip that has pins on some sides. It doesn't know about other components, it just wants to know where its own pins are. input1Side stores the side of the first input pin. Naming a variable after its value is not useful: Side top = Side.TOP; doesn't tell us anything interesting.
This might make it more clear. Calling new AndGate(LEFT, RIGHT, TOP); will create a new AND gate with an input pin on the left of the component, an input pin on the right of the component, and an output pin on the top of the component. Does that make more sense?
Making the actual connection between two components is not the responsibility of the components in this case, Grid is going to be responsible for that.
For the Signal Enum I used ints: LOW(0), HIGH(1), OPEN(9).
The reason for that is that it is required by the assignment to declare them as "0, 1, Open (For the Open I just use the number 9 to be able to use ints)".
Also: our AND Gate has 3 Inputs. Most of the time it will just have two active inputs with the third input doing nothing. But we also have to have the capability to have the AND gate react to 3 inputs (1, 1, 1 => 1 | 1, 1, 0 => 0 | etc.)
Now my question would be: Since I do not use boolean for LOW and HIGH, is the comparison still possible? for example in this:
Can I still use the exclamation mark on ints as well?