在过去的几天里我一直在看数字技术。这引起了我的兴趣。我想要实现一个程序,模拟电路技术的基本组成部分和他们的功能。我开发了一个关于如何在面向对象的层次上实现电路技术元素的小方法。这是一个好的思维方式,你可以继续工作吗?或者,一个人能在代码中更优雅地描述构建块吗?
/* objects of classes that implement this interface can be used as signaler */
public interface Output {
public boolean getOutput();
}public class Signal implements Output {
private boolean signal;
public Signal(boolean value) {
setSignal(value);
}
public void setSignal(boolean value) {
signal = value;
}
public boolean getOutput() {
return signal;
}
}abstract public class LinkingElement {
Output signal1;
Output signal2;
public LinkingElement(Output signal1, Output signal2) {
this.signal1 = signal1;
this.signal2 = signal2;
}
}public class And extends LinkingElement implements Output {
public And(Output signal1, Output signal2) {
super(signal1, signal2);
}
public boolean getOutput() {
return signal1.getOutput() && signal2.getOutput();
}
}public class Or extends LinkingElement implements Output {
public Or(Output signal1, Output signal2) {
super(signal1, signal2);
}
public boolean getOutput() {
return signal1.getOutput() || signal2.getOutput();
}
}public class LightBulb {
private Output signal;
public LightBulb(Output signal) {
this.signal = signal;
}
public void isActive() {
if(signal.getOutput()) {
System.out.println("The lightbulb is shining.");
} else {
System.out.println("The lightbulb is switched off.");
}
}
}这个类只用于测试:
public class Main {
public static void main(String[] args) {
Signal s1 = new Signal(true);
Signal s2 = new Signal(true);
Signal s3 = new Signal(true);
Signal s4 = new Signal(false);
And a1 = new And(s1, s2);
Or a2 = new Or(s3, s4);
And a3 = new And(a1, a2);
LightBulb lightBulb = new LightBulb(a3);
lightBulb.isActive();
}
}发布于 2017-12-09 22:30:08
@SimonForsberg的评论指出了我解决方案中的一些问题:
enum常量的顺序
Signal output = inputs.values().stream() // Java8 functional programming
.max((s1, s2) -> s1.compareTo(s2))// depends on order in enum!
.get();与其获取输入信号的“最大值”,不如检查Signal.ON在input值中是否出现,否则返回Signal.OFF。使用Java8流也可以轻松地解决这个问题:
Signal output = inputs.values().stream() // Java8 functional programming
.filter((s) -> s.equals(Signal.ON))// keeps only Signal.ON
.findFirst() // returns an Optional
.orElse(Signal.OFF);这意味着其他代码可以在构造函数完成对象初始化之前访问正在构造的对象。我在这里做的是:
class LightBulb implements SignalReceiver {
public LightBulb(SignalSource signalSource) {
signalSource.triggers(this); // leaks "this"
}问题是signalSource可能会立即调用在由LightBulb实现的SignalReceiver中定义的方法consume()。在这个具体的示例中没有问题,但是如果代码看起来像这样呢:
class LightBulb implements SignalReceiver {
private Signal status;
public LightBulb(SignalSource signalSource) {
signalSource.triggers(this);
status = Signal.OFF;
}
@Override
public void consume(Signal signal, SignalSource from) {
if(!status.equals(signal)) { // NullPointerException
System.out.println("light is " + signal);
status=signal;
}
}
}这将引发NullPointerException,以防triggers()的实现立即调用consume()。这样的虫子很难找到。
建议的解决方案不是传递this,而是传递匿名内部类对象或_lambda_like,即:
public LightBulb(SignalSource signalSource) {
signalSource.triggers((signal,source)->consume(signal,source));
}但这只是解决方案的一半。还必须在不同的线程中执行对自己方法的调用,以便当前线程获得完成初始化的时间:
public LightBulb(SignalSource signalSource) {
signalSource.triggers(
(signal,source)->
new Thread(()->consume(signal,source))
.start());
}虽然这不能保证我的例子中的NPE永远不会被提出。这是因为它取决于triggers()方法中代码的复杂性、系统加载以及其他最有可能隐藏的条件。但它却大大降低了可能性。
因此,最好的方法是根本不调用构造函数参数的方法,特别是当它们需要传递this时。
但在我们的示例中,问题是LogicElement对象需要知道它们的所有输入。所以我们必须从三个邪恶的选择中选择:
SignalSource对象传递给SignalReceiver构造函数,并在那里注册,可能会导致this泄漏。SignalSource对象传递给SignalReceiver构造函数,但在构造函数之外向它们注册,可能会在另一个地方忘记这一点。SignalSource对象传递给SignalReceiver构造函数并同时执行这两种操作,在另一个位置向SignalSource对象注册SignalReceiver,反之亦然。这样做的缺点是,我们必须用一个额外的方法扩展SignalReceiver的接口,这对于所有的实现来说都是不必要的。所以,是的:您应该避免调用构造函数参数上的方法,将this传递给它们,但是有时它是次要的.
我的方法有可能重复代码。即:和门看起来几乎和Or类一样。解决这一问题的方法是创建一个具有公共行为并注入差异的泛型类。
因此,我首先创建了类And:
class And implements LogicElement {
private final Collection<SignalReceiver> receivers = new HashSet<>();
private final Map<SignalSource, Signal> inputs = new HashMap<>();
And(SignalSource... signalSources) { // "vararg" parameter, accepts many sources
for (SignalSource signalSource : signalSources) {
signalSource.triggers(this);
inputs.put(signalSource, Signal.OFF);
}
}
@Override
public void triggers(SignalReceiver r) {
receivers.add(r);
}
@Override
public void consume(Signal signal, SignalSource from) {
inputs.put(from, signal);
Signal output = inputs.values().stream() // Java8 functional programming
.filter((s) -> s.equals(Signal.OFF))// keeps only Signal.ON
.findFirst() // returns an Optional
.orElse(Signal.ON);
for (SignalReceiver signalReceiver : receivers) {
signalReceiver.consume(output, this);
}
}
}唯一的区别是常数Signal.OFF和Signal.ON的位置,它们不是行为,而是数据。这意味着只有一个类(定义行为)是通过构造函数配置的:
class MultiInputLogic implements LogicElement {
private final Signal signalTorReturnForEmptyFilteredInput ;
private final Signal signalToFilter ;
private final Collection<SignalReceiver> receivers = new HashSet<>();
private final Map<SignalSource, Signal> inputs = new HashMap<>();
MultiInputLogic(Signal signalTorReturnForEmptyFilteredInput, Signal signalToFilter ,SignalSource... signalSources) {
this.signalTorReturnForEmptyFilteredInput = signalTorReturnForEmptyFilteredInput;
this.signalToFilter = signalToFilter;
for (SignalSource signalSource : signalSources) {
signalSource.triggers(this);
inputs.put(signalSource, Signal.OFF);
}
}
@Override
public void triggers(SignalReceiver r) {
receivers.add(r);
}
@Override
public void consume(Signal signal, SignalSource from) {
inputs.put(from, signal);
Signal output = inputs.values().stream() // Java8 functional programming
.filter((s) -> s.equals(signalToFilter))// keeps only Signal.ON
.findFirst() // returns an Optional
.orElse(signalTorReturnForEmptyFilteredInput);
for (SignalReceiver signalReceiver : receivers) {
signalReceiver.consume(output, this);
}
}
}但是当我们使用这个新类时,这会导致另一个“问题”:
new LightBulb(new MultiInputLogic(Signal.OFF,Signal.ON, s1, s2)); // and
new LightBulb(new MultiInputLogic(Signal.ON,Signal.OFF, s1, s2)); // or两个相同类型的新参数的顺序决定了实现了什么函数。这就是所谓的原始痴迷。为了解决这个问题,我们需要创建更多的数据对象,例如作为一个新的enum:
enum Logical{
And{
Signal getFilterSignal(){
return Signal.ON;
}
Signal getReturnSignal(){
return Signal.OFF;
},
OR{
Signal getFilterSignal(){
return Signal.OFF;
}
Signal getReturnSignal(){
return Signal.ON;
};
abstract Signal getFilterSignal();
abstract Signal getReturnSignal();
}我们更改类MultiInputLogic的构造函数:
MultiInputLogic(Logical function,SignalSource... signalSources) {
this.signalTorReturnForEmptyFilteredInput = function.getReturnSignal();
this.signalToFilter = function.getFilterSignal();接下来的用法是:
new LightBulb(new MultiInputLogic(Logical.AND, s1, s2)); // and
new LightBulb(new MultiInputLogic(Logical.OR, s1, s2)); // or发布于 2017-12-09 12:31:07
谢谢你分享你的代码。
你发布的是一个很好的数据结构。
但OOP是关于行为的。(我们仍然需要数据结构来存储状态,但是对象通常有行为。
让我们看看,电子系统中有三种类型的组件:
信号不是对象(有行为,因为它们只是在对象之间传递的消息.
所以我建议这样的课程:
// signal are still data structure
enum Signal { OFF, ON }
// some interfaces for abstraction
interface SignalReceiver {
void consume(Signal signal, SignalSource from);
}
interface SignalSource {
void triggers(SignalReceiver r);
}
interface LogicElement extends SignalReceiver, SignalSource {}// a switch is a typica "signalSource"
class Switch implements SignalSource {
private final Collection<SignalReceiver> receivers = new HashSet<>();
private Signal state = Signal.OFF;
private final String name;
public Switch(String name) {
super();
this.name = name;
}
@Override
public void triggers(SignalReceiver r) {
receivers.add(r);
}
public void doSwitch() {
state = Signal.values()[1 - state.ordinal()]; // get the other signal
System.out.print("Switch "+name+" is "+state +" ");
for (SignalReceiver signalReceiver : receivers) {
signalReceiver.consume(state, this);
}
}
}class LightBulb implements SignalReceiver {
public LightBulb(SignalSource signalSource) {
signalSource.triggers(this); // tells the source "trigger me!"
}
@Override
public void consume(Signal signal, SignalSource from) {
System.out.println("light is " + signal);
}
}class Or implements LogicElement {
private final Collection<SignalReceiver> receivers = new HashSet<>();
private final Map<SignalSource, Signal> inputs = new HashMap<>();
Or(SignalSource... signalSources) { // "vararg" parameter, accepts many sources
for (SignalSource signalSource : signalSources) {
signalSource.triggers(this); // tells the source "trigger me!"
inputs.put(signalSource, Signal.OFF);
}
}
@Override
public void triggers(SignalReceiver r) {
receivers.add(r);
}
@Override
public void consume(Signal signal, SignalSource from) {
inputs.put(from, signal);
Signal output = inputs.values().stream() // Java8 functional programming
.max((s1, s2) -> s1.compareTo(s2))// depends on order in enum!
.get();
for (SignalReceiver signalReceiver : receivers) {
signalReceiver.consume(output, this);
}
}
}这是一个考验:
public class Electronics {
public static void main(String[] args) {
// circuit configuration
Switch s1 = new Switch("left");
Switch s2 = new Switch("right");
new LightBulb(new Or(s1, s2));
// operation
s1.doSwitch(); // light on
s1.doSwitch(); // light off
s2.doSwitch(); // light on
s1.doSwitch(); // light on
s2.doSwitch(); // light on
s1.doSwitch(); // light off
}
}Yo可能会注意到我的方法没有"getter“方法。
这是因为"getter“和"setter”违反了最重要的OO原则之一:信息隐藏/封装。对于实现实际行为的类(对于纯数据对象,它们是可以的.),您应该避免它们。“经验法则”是:“说吧,别问!”
https://codereview.stackexchange.com/questions/182387
复制相似问题