首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java中的电子元器件

Java中的电子元器件
EN

Code Review用户
提问于 2017-12-08 21:49:19
回答 2查看 424关注 0票数 4

在过去的几天里我一直在看数字技术。这引起了我的兴趣。我想要实现一个程序,模拟电路技术的基本组成部分和他们的功能。我开发了一个关于如何在面向对象的层次上实现电路技术元素的小方法。这是一个好的思维方式,你可以继续工作吗?或者,一个人能在代码中更优雅地描述构建块吗?

Output.java

代码语言:javascript
复制
/* objects of classes that implement this interface can be used as signaler */

public interface Output {
    public boolean getOutput();
}

Signal.java

代码语言:javascript
复制
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;
    }
}

LinkingElements.java

代码语言:javascript
复制
abstract public class LinkingElement {
    Output signal1;
    Output signal2;

    public LinkingElement(Output signal1, Output signal2) { 
        this.signal1 = signal1;
        this.signal2 = signal2;
    }
}

And.java

代码语言:javascript
复制
public class And extends LinkingElement implements Output {
    public And(Output signal1, Output signal2) { 
        super(signal1, signal2);
    }

    public boolean getOutput() {
        return signal1.getOutput() && signal2.getOutput();
    }
}

Or.java

代码语言:javascript
复制
public class Or extends LinkingElement implements Output {
    public Or(Output signal1, Output signal2) {
        super(signal1, signal2);
    }

    public boolean getOutput() {
        return signal1.getOutput() || signal2.getOutput();
    }
}

LightBulb.java

代码语言:javascript
复制
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.");
        }
    }
}

Main.java

这个类只用于测试:

代码语言:javascript
复制
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();
    }
}
EN

回答 2

Code Review用户

回答已采纳

发布于 2017-12-09 22:30:08

@SimonForsberg的评论指出了我解决方案中的一些问题:

取决于enum常量

的顺序

代码语言:javascript
复制
    Signal output = inputs.values().stream() // Java8 functional programming
            .max((s1, s2) -> s1.compareTo(s2))// depends on order in enum!
            .get();

与其获取输入信号的“最大值”,不如检查Signal.ONinput值中是否出现,否则返回Signal.OFF。使用Java8流也可以轻松地解决这个问题:

代码语言:javascript
复制
    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);

构造函数泄漏

这意味着其他代码可以在构造函数完成对象初始化之前访问正在构造的对象。我在这里做的是:

代码语言:javascript
复制
class LightBulb implements SignalReceiver {
    public LightBulb(SignalSource signalSource) {
        signalSource.triggers(this); // leaks "this"
    }

问题是signalSource可能会立即调用在由LightBulb实现的SignalReceiver中定义的方法consume()。在这个具体的示例中没有问题,但是如果代码看起来像这样呢:

代码语言:javascript
复制
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,即:

代码语言:javascript
复制
    public LightBulb(SignalSource signalSource) {
        signalSource.triggers((signal,source)->consume(signal,source));
    }

但这只是解决方案的一半。还必须在不同的线程中执行对自己方法的调用,以便当前线程获得完成初始化的时间:

代码语言:javascript
复制
    public LightBulb(SignalSource signalSource) {
        signalSource.triggers(
            (signal,source)-> 
                 new Thread(()->consume(signal,source))
                 .start());
    }

虽然这不能保证我的例子中的NPE永远不会被提出。这是因为它取决于triggers()方法中代码的复杂性、系统加载以及其他最有可能隐藏的条件。但它却大大降低了可能性。

因此,最好的方法是根本不调用构造函数参数的方法,特别是当它们需要传递this时。

但在我们的示例中,问题是LogicElement对象需要知道它们的所有输入。所以我们必须从三个邪恶的选择中选择:

  1. SignalSource对象传递给SignalReceiver构造函数,并在那里注册,可能会导致this泄漏。
  2. SignalSource对象传递给SignalReceiver构造函数,但在构造函数之外向它们注册,可能会在另一个地方忘记这一点。
  3. 不将SignalSource对象传递给SignalReceiver构造函数并同时执行这两种操作,在另一个位置向SignalSource对象注册SignalReceiver,反之亦然。这样做的缺点是,我们必须用一个额外的方法扩展SignalReceiver的接口,这对于所有的实现来说都是不必要的。

所以,是的:您应该避免调用构造函数参数上的方法,将this传递给它们,但是有时它是次要的.

重复代码

我的方法有可能重复代码。即:和门看起来几乎和Or类一样。解决这一问题的方法是创建一个具有公共行为并注入差异的泛型类。

因此,我首先创建了类And

代码语言:javascript
复制
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.OFFSignal.ON的位置,它们不是行为,而是数据。这意味着只有一个类(定义行为)是通过构造函数配置的:

代码语言:javascript
复制
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);
        }
    }
}

但是当我们使用这个新类时,这会导致另一个“问题”:

代码语言:javascript
复制
    new LightBulb(new MultiInputLogic(Signal.OFF,Signal.ON, s1, s2)); // and
    new LightBulb(new MultiInputLogic(Signal.ON,Signal.OFF, s1, s2)); // or

两个相同类型的新参数的顺序决定了实现了什么函数。这就是所谓的原始痴迷。为了解决这个问题,我们需要创建更多的数据对象,例如作为一个新的enum

代码语言:javascript
复制
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的构造函数:

代码语言:javascript
复制
MultiInputLogic(Logical  function,SignalSource... signalSources) {
            this.signalTorReturnForEmptyFilteredInput = function.getReturnSignal();
            this.signalToFilter = function.getFilterSignal();

接下来的用法是:

代码语言:javascript
复制
    new LightBulb(new MultiInputLogic(Logical.AND, s1, s2)); // and
    new LightBulb(new MultiInputLogic(Logical.OR, s1, s2)); // or
票数 1
EN

Code Review用户

发布于 2017-12-09 12:31:07

谢谢你分享你的代码。

你发布的是一个很好的数据结构。

但OOP是关于行为的。(我们仍然需要数据结构来存储状态,但是对象通常有行为。

让我们看看,电子系统中有三种类型的组件:

  1. 信号源(如开关和传感器)
  2. 信号接收器(如灯泡、LED或电动发动机)
  3. 逻辑元素(如And或NOT)。

信号不是对象(有行为,因为它们只是在对象之间传递的消息.

所以我建议这样的课程:

代码语言:javascript
复制
// 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 {}
代码语言:javascript
复制
// 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);
        }
    }
}
代码语言:javascript
复制
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);
    }
}
代码语言:javascript
复制
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);
        }
    }
}

这是一个考验:

代码语言:javascript
复制
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原则之一:信息隐藏/封装。对于实现实际行为的类(对于纯数据对象,它们是可以的.),您应该避免它们。“经验法则”是:“说吧,别问!”

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/182387

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档