Java中的多重继承设计问题

发布于 2024-08-19 15:08:22 字数 1334 浏览 2 评论 0原文

你如何处理java中只有单一继承的情况?这是我的具体问题:

我有三个(简化的)类:

public abstract class AbstractWord{
    String kind;    // eg noun, verb, etc

    public String getKind(){ return kind; }

}

public class Word extends AbstractWord{
    public final String word;

    ctor...

    public void setKind(){
        // based on the variable word calculate kind..
    }
}

public class WordDescriptor extends AbstractWord{

    ctor..

    public void setKind(String kind){this.kind = kind;}

}

这是我认为最基本的实现,但我想进行其他实现。

假设我想添加一个新变量,例如 wordLength,但我想使用继承来添加它。这意味着我不想修改原始的 AbstractWord 类。即:

public class Length{
    private int length;

    public int getLength(){return length};
}

public class BetterWord extends AbstractWord AND Length{

    public void setLength(){
        // based on the variable word calculate Length..
    }
}

public class BetterWordDescriptor extends AbstractWord AND length{

    public void setLength(int length){this.length = length;}
}

我知道 java 不允许我这样做,但它使我的代码非常丑陋。现在,每当我添加一个字段时,我只是将其添加到 AbstractWord,但随后我需要重命名该 AbstractWord(以及 Word 和 WordDescriptor)。 (由于向后兼容性,我不能只将该字段添加到另一个字段,它会破坏 equals 方法和类似的内容)。

这似乎是一个非常常见的设计问题,但我绞尽脑汁却无法想出任何漂亮的解决方案。

有没有一种设计模式可以解决这个问题?我有一些潜在的解决方案,但我想看看是否有我遗漏的东西。

谢谢, 杰克

更新:长度是指单词中的音节数(抱歉不清楚)

How do you deal with having only single inheritance in java? Here is my specific problem:

I have three (simplified) classes:

public abstract class AbstractWord{
    String kind;    // eg noun, verb, etc

    public String getKind(){ return kind; }

}

public class Word extends AbstractWord{
    public final String word;

    ctor...

    public void setKind(){
        // based on the variable word calculate kind..
    }
}

public class WordDescriptor extends AbstractWord{

    ctor..

    public void setKind(String kind){this.kind = kind;}

}

This is what I consider my most basic implementation but I want to make other implementations.

Lets say that I want to add a new variable say wordLength but I want to add it using inheritance. Meaning I do NOT want modify that original AbstractWord class. Ie something along the lines of this:

public class Length{
    private int length;

    public int getLength(){return length};
}

public class BetterWord extends AbstractWord AND Length{

    public void setLength(){
        // based on the variable word calculate Length..
    }
}

public class BetterWordDescriptor extends AbstractWord AND length{

    public void setLength(int length){this.length = length;}
}

I know that java does not let me do this but it has made my code very ugly. Right now whenever I add a field I am just adding it to AbstractWord but then I either need to rename that AbstractWord (and Word and WordDescriptor). (I can't just add the field to the other one because of backwards compatibility, it break equals methods and stuff like that).

This seems like a pretty common design issue but I have racked my head and I cannot come up with any beautiful solutions.

Is there a design pattern that addresses this? I have some potential solutions but I wanted to see if there was something that I was missing.

thanks,
Jake

Update: Length refers to the number of syllables in the word (sorry about the lack of clarity)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

眸中客 2024-08-26 15:08:22

优先考虑组合而不是继承。

解决方案考虑到可能存在另一种类型的单词可能需要 WordLengthSupport。

类似地,可以创建和实现其他接口,并且各种单词类型可以混合和匹配这些接口。

public class WordLength {
    private int length = 0;
    public int getLength(){return length};
    public void setLength(int length){this.length = length};
}

public interface WordLengthSupport {
    public WordLength getWordLength();
}

public class BetterWord extends AbstractWord 
        implements WordLengthSupport {
    WordLength wordLength;
    public WordLength getWordLength() {
        if(wordLength==null) {
            // each time word changes 
            // make sure to set wordLength to null
            calculateWordLength(); 
        }
        return wordLength;
    }
    private void calculateWordLength() {
        // This method should be 
        //    called in constructor 
        //    or each time word changes 
        int length = // based on the variable word calculate Length..
        this.wordLength = new WordLength();
        this.wordLength.setLength(length);
    }
}

public class BetterWordDescriptor extends AbstractWord  
        implements WordLengthSupport {
    WordLength wordLength;
    public WordLength getWordLength(return wordLength);
    public void setWordLength(WordLength wordLength) {
        // Use this to populate WordLength of respective word
        this.wordLength = wordLength;
    }
}

策略模式定义了一系列算法,封装了每个算法,并使它们可以互换。策略使算法能够独立于使用它的客户端而变化。

该解决方案不使用策略模式,但可以对其进行重构。

Favor composition over inheritance.

Solution takes into consideration that there could be another type of word that may need WordLengthSupport.

Similarly other interfaces could be created and implemented and various word types can have mix and match of those interfaces.

.

public class WordLength {
    private int length = 0;
    public int getLength(){return length};
    public void setLength(int length){this.length = length};
}

.

public interface WordLengthSupport {
    public WordLength getWordLength();
}

.

public class BetterWord extends AbstractWord 
        implements WordLengthSupport {
    WordLength wordLength;
    public WordLength getWordLength() {
        if(wordLength==null) {
            // each time word changes 
            // make sure to set wordLength to null
            calculateWordLength(); 
        }
        return wordLength;
    }
    private void calculateWordLength() {
        // This method should be 
        //    called in constructor 
        //    or each time word changes 
        int length = // based on the variable word calculate Length..
        this.wordLength = new WordLength();
        this.wordLength.setLength(length);
    }
}

.

public class BetterWordDescriptor extends AbstractWord  
        implements WordLengthSupport {
    WordLength wordLength;
    public WordLength getWordLength(return wordLength);
    public void setWordLength(WordLength wordLength) {
        // Use this to populate WordLength of respective word
        this.wordLength = wordLength;
    }
}

.

The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

This solution does not use strategy pattern but can be refactored for same.

囚我心虐我身 2024-08-26 15:08:22

只需使用组合而不是继承:

has-a Length<的 BetterWord is-an AbstractWord /code>:

public class BetterWord extends AbstractWord {

    private Length length;

    public void setLength(int value){
        length.setLength(value);
    }
}

编辑

如果 API 需要 Length 类型的对象,只需添加一个 getter:

public class BetterWord extends AbstractWord {

    private Length length;

    public void setLength(int value){
        length.setLength(value);
    }

    public Length getLength() {
        return length
    }
}

或者将实现 Length 重命名为 LengthImpl 并定义接口Length,因为一个类可以实现多个接口。

Just use composition instead of inheritance:

a BetterWord is-an AbstractWord that has-a Length:

public class BetterWord extends AbstractWord {

    private Length length;

    public void setLength(int value){
        length.setLength(value);
    }
}

EDIT

If the API needs an object of type Length, just add a getter:

public class BetterWord extends AbstractWord {

    private Length length;

    public void setLength(int value){
        length.setLength(value);
    }

    public Length getLength() {
        return length
    }
}

Or rename the implementation Length to LengthImpl and define an interface Length, because a class can implement multiple interfaces.

乱世争霸 2024-08-26 15:08:22

根据您的具体示例,您可以将 装饰器 模式与接口结合使用来补充您的 具有附加功能的Word类;例如,

// *Optional* interface, useful if we wish to reference Words along with
// other classes that support the concept of "length".
public interface Length {
  int getLength();
}

// Decorator class that wraps another Word and provides additional
// functionality.  Could add any additional fields here too.
public class WordExt extends AbstractWord implements Length {
  private final Word word;

  public class(Word word) {
    this.word = word;
  }

  public int getLength() {
    return word.getKind().length();
  }
}

此外值得注意的是,Java 中缺乏多重继承并不是这里真正的问题;这更多的是重新设计你的设计。一般来说,过度使用继承被认为是不好的做法,因为深层继承层次结构难以解释/维护。

With your specific example you could use the decorator pattern in conjunction with interfaces to supplement your Word class with additional functionality; e.g.

// *Optional* interface, useful if we wish to reference Words along with
// other classes that support the concept of "length".
public interface Length {
  int getLength();
}

// Decorator class that wraps another Word and provides additional
// functionality.  Could add any additional fields here too.
public class WordExt extends AbstractWord implements Length {
  private final Word word;

  public class(Word word) {
    this.word = word;
  }

  public int getLength() {
    return word.getKind().length();
  }
}

In addition it's worth noting that the lack of multiple inheritence in Java isn't really the issue here; it's more a case of reworking your design. In general it's considered bad practice to over-use inheritence as deep inheritence hierarchies are difficult to interpret / maintain.

层林尽染 2024-08-26 15:08:22

看了之后,我的第一感觉就是你的模型有点复杂。

单词有一个 String 来描述存储在 Word 对象中的单词本身,还有一个类来表示它是名词、动词、形容词等。Word 的另一个属性是存储在 Word 对象中的字符串的长度。

从“is-a”和“has-a”关系的角度来思考事物,您可以消除很多复杂性。

例如,为什么需要一个扩展 AbstractWord 的 WordDescriptor?一个词会从动词变成形容词吗?我本以为单词类型是在创建对象时设置的,并且在 Word 对象的生命周期内不会改变。一旦您有了单词“Australia”的 Word 对象,单词的种类在该对象的生命周期内就不会改变。

嗯。在用“动词”类型实例化对象来描述狗发出的声音后,也许您可​​能有一个代表“吠叫”一词的 Word 对象。然后您意识到您实际上需要 Word 对象来表示描述树的覆盖物的名词。有可能,但是狗皮和树皮都可以存在。

所以我认为您选择的模型有点太复杂,您的问题可以通过返回并简化原始对象模型来解决。

首先针对基本模型的每个继承方面问自己一个问题。

当我说 B 类扩展了 A 类时,我可以说 B 类“是”A 类并且我正在专门化它的行为吗?

例如,可以扩展基类 Animal 以提供特殊的袋鼠类。然后你可以说“the kangaroo “is-a” Animal。你正在专门化行为。

然后看看属性,袋鼠有一个 Location 属性来描述它的发现位置。然后你可以说 a Kangaroo “has-a” “位置。袋鼠的“is-a”位置没有意义。

同样,单词“has-a”长度。并且单词“is-a”长度的陈述没有意义。

顺便说一句,所有澳大利亚参考文献写这篇文章是为了庆祝今天的 1 月 26 日澳大利亚国庆日

Looking at it, my first feeling is that your model is a bit complicated.

A word has a String to describe the word itself being stored in the Word object along with a class to say it's a noun, verb, adjective, etc. Another property of a Word is the length of the string stored in the Word object.

Think of things in terms of "is-a" and "has-a" relationships and you can remove a lot of complexity.

For instance why do you need a WordDescriptor that extends AbstractWord? Is a word going to change from a verb to an adjective? I would have thought that the word type was set when the object was created and would not change during the lifetime of the Word object. Once you had a Word object for the word "Australia" the Kind of word would not change for the lifetime of the object.

Hmmm. Maybe you might have a Word object representing the word "bark" after instantiating the object with a type of "verb" to describe the sound a dog makes. Then you realise that you actually needed to have the Word object to represent a noun that describes the covering of a tree. Possible, but both the dog's bark and the tree's bark can exist.

So I think the model you've chosen is a bit too complicated and that your question can be resolved by going back and simplifying your original object model.

Start by asking yourself a question for each of the inheritance aspects of your basic model.

When I say Class B extends Class A, can I say that Class B "is-a" Class A and that I am specialising its behaviour?

For example, a base class Animal can be extended to provide the specialised class of Kangaroo. Then you can say that "the kangaroo "is-a" Animal. You are specialising the behaviour.

Then look at the attributes, a Kangaroo has a Location attribute to describe where it is found. Then you can say a Kangaroo "has-a" location. A Kangaroo "is-a" location doesn't make sense.

Similarly, a Word "has-a" length. And the statement a Word "is-a" length just doesn't make sense.

BTW All Australian references in this post are deliberate to celebrate Australia Day which is today 26th January!

HTH

并安 2024-08-26 15:08:22

(由于向后兼容性,我不能只将该字段添加到另一个字段,它会破坏 equals 方法和类似的内容)。

它不会破坏源兼容性。除非你在 equals 方法中做了一些非常疯狂的事情。

重命名类通常不是处理二进制兼容性的方法。

(I can't just add the field to the other one because of backwards compatibility, it break equals methods and stuff like that).

It won't break source compatibility. Not unless you're doing something really crazy in your equals methods.

And renaming your classes is generally not the way to handle binary compatibility.

情泪▽动烟 2024-08-26 15:08:22

问题不在于“如何处理单一继承”。您缺少的并不是真正的设计模式,而是学习将 API 与实现分开设计。

我会像这样实现它:

public interface WordDescriptor {
    void getKind();
    Word getWord();
}

public interface Word {
    String getWord();
}

public class SimpleWord implements Word {
    private String word;

    public SimpleWord(String word) { this.word = word; }
    public String getWord() { return word; }
}

public class SimpleWordDescriptor implements WordDescriptor {
    private Word word;
    private String kind;

    public SimpleWordDescriptor(Word word, String kind) {
        this.word = word;
        this.kind = kind; // even better if WordDescriptor can figure it out internally
    }

    public Word getWord() { return word; }

    public String getKind() { return kind; }
}

通过这个基本设置,当您想引入长度属性时,您所要做的就是:

public interface LengthDescriptor {
    int getLength();
}

public class BetterWordDescriptor extends SimpleWordDescriptor
                                  implements LengthDescriptor {
    public BetterWordDescriptor(Word word, String kind) {
        super(word, kind);
    }

    public int getLength() { getWord().length(); }        
}

使用属性组合以及装饰器模式的其他答案也是您的完全有效的解决方案问题。您只需要确定您的对象是什么、它们的“可组合性”程度以及如何使用它们 - 因此首先设计 API。

The problem is not "how to deal with single inheritance". What you're missing is not really a design pattern but learning to design the API separately from the implementation.

I would implement it like so:

public interface WordDescriptor {
    void getKind();
    Word getWord();
}

public interface Word {
    String getWord();
}

public class SimpleWord implements Word {
    private String word;

    public SimpleWord(String word) { this.word = word; }
    public String getWord() { return word; }
}

public class SimpleWordDescriptor implements WordDescriptor {
    private Word word;
    private String kind;

    public SimpleWordDescriptor(Word word, String kind) {
        this.word = word;
        this.kind = kind; // even better if WordDescriptor can figure it out internally
    }

    public Word getWord() { return word; }

    public String getKind() { return kind; }
}

With this basic setup, when you want to introduce a length property, all you have to do is this:

public interface LengthDescriptor {
    int getLength();
}

public class BetterWordDescriptor extends SimpleWordDescriptor
                                  implements LengthDescriptor {
    public BetterWordDescriptor(Word word, String kind) {
        super(word, kind);
    }

    public int getLength() { getWord().length(); }        
}

The other answers that uses composition of properties as well as the Decorator pattern are also entirely valid solutions to your problem. You just need to identify what your objects are and how "composable" they are, and how they are to be used - hence designing the API first.

巷子口的你 2024-08-26 15:08:22

/**
* 第一个例子
*/

class FieldsOfClassA {
    public int field1;
    public char field2;
}

interface IClassA {
    public FieldsOfClassA getFieldsA();
}

class CClassA implements IClassA {
    private FieldsOfClassA fields;

    @Override
    public FieldsOfClassA getFieldsA() {
        return fields;
    }
}

/**
 * seems ok for now
 * but let's inherit this sht
 */


class FieldsOfClassB {

    public int field3;
    public char field4;
}

interface IClassB extends IClassA {

    public FieldsOfClassA getFieldsA();
    public FieldsOfClassB getFieldsB();
}

class CClassB implements IClassB {

    private FieldsOfClassA fieldsA;
    private FieldsOfClassB fieldsB;

    @Override
    public FieldsOfClassA getFieldsA() {
        return fieldsA;
    }

    @Override
    public FieldsOfClassB getFieldsB() {
        return fieldsB;
    }
}

/**

  • 哇这个怪物变大了

  • 想象一下你需要4级继承

  • 写这个地狱要花很多时间

  • 我什至没有说那些 iface 的用户会认为

  • 我需要哪些字段 fieldsA fieldsB fieldsC 或另一个字段

所以组合在这里不起作用
你的可悲尝试是没有用的,

当你考虑面向对象编程时,

你需要具有 6-7 级多重继承的大模型

,因为这是很好的测试,并且因为对应于现实生活的模型或经过文明 4 千年测试的数学模型。

如果您的模型需要 2 级继承,请停止假装您使用 OO

您可以使用任何语言轻松实现它,甚至是像 C 或 Basic 语言这样的过程语言
*/

/**
* First example
*/

class FieldsOfClassA {
    public int field1;
    public char field2;
}

interface IClassA {
    public FieldsOfClassA getFieldsA();
}

class CClassA implements IClassA {
    private FieldsOfClassA fields;

    @Override
    public FieldsOfClassA getFieldsA() {
        return fields;
    }
}

/**
 * seems ok for now
 * but let's inherit this sht
 */


class FieldsOfClassB {

    public int field3;
    public char field4;
}

interface IClassB extends IClassA {

    public FieldsOfClassA getFieldsA();
    public FieldsOfClassB getFieldsB();
}

class CClassB implements IClassB {

    private FieldsOfClassA fieldsA;
    private FieldsOfClassB fieldsB;

    @Override
    public FieldsOfClassA getFieldsA() {
        return fieldsA;
    }

    @Override
    public FieldsOfClassB getFieldsB() {
        return fieldsB;
    }
}

/**

  • wow this monster got bigger

  • imagine that you will need 4 lvl of inheritance

  • it would take so much time to write this hell

  • I'm even not talking that user of those iface will think

  • what fields i will need fieldsA fieldsB fieldsC or another one

So composition does not work here
and your pathetic tries are useless

When u think about Oject Oriented programming

u need BIG models with 6-7 lvls of multiple inheritance

because that is good test and because corresponds to models of real life or math models tested by civilization for 4 thousands years.

If your models require 2 lvl of inheritance stop pretending u using OO

U can easily implement it with any language even procedural one like C or Basic language
*/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文