在 class 中创建实例或从外部获取对象,哪个是正确的?

Create instance in class or get object from outside, which on is true?

我研究了 SOLID 原理并为我创建了一个关于它的问题。 你假设我们在 Mug class 中需要一个 Tea 对象,现在这比在 Mug class 中从 Tea 创建一个实例更好还是通过 Constructorsetter 方法从外部传递。

哪个是正确的?

示例:

class Mug {

    private Tea tea;

    public Mug(){
       this.tea = new Tea();
    }

    public boolean isFull(){
       return this.tea.value != 10;
    }
}

class Mug {

    private Tea tea;

    public Mug(Tea tea){
       this.tea = tea;
    }

   // public void setTea(Tea tea){
   //    this.tea = tea;
   // }

    public boolean isFull(){
       return this.team.value != 10;
    }
}

使用:

  public class Test {

   static void main(String[] args){
       Mug mug = new Mug();
       //or
       Mug mug = new Mug(new Tea());
  }
}

哪个更好?

注意:假设Mug在我们的程序中只支持Tea对象。

你的两个案例都违反了 SOLID。

你的每个具体实现(具体 class)应该只依赖于抽象。 在你的情况下,茶不是抽象的。 Mug 与 Tea 紧密耦合。下面是一种可能的编码方式。

public interface ITea{
     //tea related methods which you think should be exposed to outside world. Also all implementation of ITea must support these method (L in SOLID)
 }

public class Tea implements ITea{
  // Implement the contract methods from ITea
}

public class Mug {
    private ITea tea;
    // Have constructor or setter to inject concrete implementation. Setter will provide you capability to modify behavioral at run time.
 }

编辑: 1.如果我们确定茶只有一种可能的实现。然后接口也更好,因为具体的实现很难模拟,因此很难进行单元测试。

  1. 避免使用枚举来设置类型。枚举注入 switch case 并且在未来如果添加了一个 case,所有这些 switch case 都需要修改,导致违反 SOLID 中的 O。

如果添加案例,您必须修改现有代码,如果您忘记在某处添加案例,则会导致意外错误。 (它违反了OCP。而不是枚举将逻辑放在具体实现中,具有通用接口和具体实现)。此外,如果我们在枚举的各个实例中实现案例特定逻辑,那么枚举文件将变得巨大。相反,如果我们在实现公共接口的具体 classes 中有特定的逻辑,那么用户代码就会变得简单,因为它可以以多态方式注入具体 classes。

p.s。这个答案是关于遵循原则的,并不总是可以这样做。违反原则是可以的,但我们必须知道我们正在违反原则,并且有充分的理由这样做

我会尽可能使用第二个,注入 classes 依赖项,以这个例子为例,其中 Tea 构造函数需要更多信息:

class Mug {

    private Tea tea;

    public Mug(int temperature) {
        this.tea = new Tea(temperature);
    }

    public boolean isFull() {
        return tea.value != 10;
    }
}

看看 Mug 的构造函数现在需要仅供 Tea 的构造函数使用的信息是多么丑陋。

但就目前而言,我会说您的两个示例都没有违反任何 SOLID 原则。

没有任何内容表明您不能在另一个对象中创建新对象,也没有任何内容表明您必须创建抽象。

这将是一个真正的违规行为:

class Mug { // VIOLATION

    private Tea tea;
    private Coffee coffee;

    public Mug(boolean tea) {
        if (tea) {
            this.tea = new Tea();
        } else {
            this.coffee = new Coffee();
        }
    }

    public boolean isFull() {
        return tea.value != 10 || coffee.value != 10;
    }
}

class 依赖于多个相似的 class。比较:

class Mug {

    private Liquid liquid;

    public Mug(Liquid liquid) {
        this.liquid = liquid;
    }

    public boolean isFull() {
        return liquid.getVolume() != 10;
    }
}

interface Liquid {
    int getVolume();
}

class Tea implements Liquid {

    private int volume;

    @Override
    public int getVolume() {
        return volume;
    }
}

class Coffee implements Liquid {

    private int volume;

    @Override
    public int getVolume() {
        return volume;
    }
}

但是因为你只有一件事可以放在 Mug 中,所以没有违规,也不需要抽象。一般来说,永远不要写任何不能解决问题的代码。

永远不要用 ITea 这样的名字创建抽象。在这种情况下,具体 class 名称不够具体,或者抽象名称不够通用。