So I was checking out the courses on InteractivePython, and I came across this example to demonstrate inheritance.
My focus is on the Connector class and how it interacts with BinaryGate and UnaryGate via the setNextPin method.
class LogicGate:
def __init__(self,n):
self.name = n
self.output = None
def getName(self):
return self.name
def getOutput(self):
self.output = self.performGateLogic()
return self.output
class BinaryGate(LogicGate):
def __init__(self,n):
LogicGate.__init__(self,n)
self.pinA = None
self.pinB = None
def getPinA(self):
if self.pinA == None:
return int(input("Enter Pin A input for gate "+self.getName()+"-->"))
else:
return self.pinA.getFrom().getOutput()
def getPinB(self):
if self.pinB == None:
return int(input("Enter Pin B input for gate "+self.getName()+"-->"))
else:
return self.pinB.getFrom().getOutput()
def setNextPin(self,source):
if self.pinA == None:
self.pinA = source
else:
if self.pinB == None:
self.pinB = source
else:
print("Cannot Connect: NO EMPTY PINS on this gate")
class AndGate(BinaryGate):
def __init__(self,n):
BinaryGate.__init__(self,n)
def performGateLogic(self):
a = self.getPinA()
b = self.getPinB()
if a==1 and b==1:
return 1
else:
return 0
class OrGate(BinaryGate):
def __init__(self,n):
BinaryGate.__init__(self,n)
def performGateLogic(self):
a = self.getPinA()
b = self.getPinB()
if a ==1 or b==1:
return 1
else:
return 0
class UnaryGate(LogicGate):
def __init__(self,n):
LogicGate.__init__(self,n)
self.pin = None
def getPin(self):
if self.pin == None:
return int(input("Enter Pin input for gate "+self.getName()+"-->"))
else:
return self.pin.getFrom().getOutput()
def setNextPin(self,source):
if self.pin == None:
self.pin = source
else:
print("Cannot Connect: NO EMPTY PINS on this gate")
class NotGate(UnaryGate):
def __init__(self,n):
UnaryGate.__init__(self,n)
def performGateLogic(self):
if self.getPin():
return 0
else:
return 1
class Connector:
def __init__(self, fgate, tgate):
self.fromgate = fgate
self.togate = tgate
tgate.setNextPin(self)
def getFrom(self):
return self.fromgate
def getTo(self):
return self.togate
def main():
g1 = AndGate("G1")
g2 = AndGate("G2")
g3 = OrGate("G3")
g4 = NotGate("G4")
c1 = Connector(g1,g3)
c2 = Connector(g2,g3)
c3 = Connector(g3,g4)
print(g4.getOutput())
main()
And so the output might look like this:
Input Pin A for gate G1 --> 0
Input Pin B for gate G1 --> 0
Input Pin A for gate G2 --> 1
Input Pin B for gate G2 --> 0
1
So everything is working as intended, but my concern is the "two-way access" between, for example, BinaryGate and Connector.
It's bothering me that BinaryGate.getPinA can call (Connector).getFrom().getOutput() in the line return self.pinA.getFrom().getOutput(), while Connector.__init__() calls BinaryGate.setNextPin
In my mind, there should only be one direction of "visibility" between the two objects - either a *Gate can access members of a Connector, or vice-versa, but not both ways at once. Is this a flawed assumption?
Does the code as-is break encapsulation? If so, how can the code be refactored to have proper encapsulation? If not, why not?
Edit:
Just to be clear for future viewers, this is not my code, it's from InteractivePython's Data Structures course.
