分层对象设计(Java)

时间:2011-10-24 10:53:21

标签: oop

我正在寻找一个类似于以下问题的优雅设计:

故事可以具有灵活的层次结构。它可能包括几本书,每本书都有章节,每章都有章节,每章都包含文字。或者,它可能只有部分(例如一个简短的故事)。或者它可能只包含有章节的章节。在任何情况下,你都不能混合使用风格(因此你不能根据书籍为故事添加章节,你必须将章节添加到书本身中。)

我已经为这类问题提出了几个设计解决方案,但它们变得混乱。是否有一种干净的方式来表示这一点,以便在参考Story类时,我可以以清晰,系统的方式访问内容?

4 个答案:

答案 0 :(得分:2)

良好的OO设计始于考虑用例而不是类层次结构。这是一个常见的错误,往往会产生过于复杂的设计。

首先考虑一下您正在构建的内容,并使用英语散文以问题域的语言描述您正在解决的问题。

如果产品是UI,则考虑制作模型。

然后你就可以开始写出用例并开始考虑对象如何相互作用。

它被称为面向对象的编程,而不是面向类的编程。类是管理/创建/运行系统中所有对象的代码规范。我会考虑对象和他们正在做的事情。类只是一个实现细节。

如果您的目标是对层次结构执行操作,则可能需要考虑使用Composite模式。您可以执行类似Story对象的操作,该对象可以包含Story对象列表。每个Story对象也有它自己的类型(书籍集,书,章,子章,段落,论文),它自己的属性和方法(取决于你的用例)。

答案 1 :(得分:1)

这种棘手的情况,因为像“书籍”,“章节”,“部分”这样的概念可能有一些共同的元素,提出了类层次结构或通用接口实现。

与此同时,作为不同的类/对象,完全不同,因为要求“所以你不能根据书籍添加章节”。

在处理与分层对象的概念时,有几种方法可以将它们转换为代码,每种方法都适合特定情况。

<强> 1。班级构成

每个概念都有一个类或原型,它们可能通过继承或接口相关或不相关。 这些元素有内部集合,可以限制它们的操作, 通过方法。

// this one can be optional, not required,
// replaced by several parent classes,
// or replaced by interfaces

public class LibraryRootClassMaybe {
  // members here
}

public class BookText extends LibraryRootClassMaybe {
  // members here
} // class BookText

public class BookSection extends LibraryRootClassMaybe {
    // element collection should not be public
  List BookTexts;

  public Book() {
    this.BookTexts = new ArrayList();
  }

  public void addBookTest(BookText Item) {
    // validation and casting from object to BookText
  }

  // members here
} // class BookSection

public class BookChapter extends LibraryRootClassMaybe {
    // element collection should not be public
  List BookSections;

  public Book() {
    this.BookSections = new ArrayList();
  }

  public void addBookTest(BookSection Item) {
    // validation and casting from object to BookSection
  }

  // members here
} // class BookChapter

public class Book extends LibraryRootClassMaybe {
    // element collection should not be public
  List BookChapters;

  public Book() {
    this.BookChapters = new ArrayList();
  }

  public void addBookTest(BookText Item) {
    // validation and casting from object to BookText
  }

  // members here
} // class Book

当没有很多不同的类时,这些方法很好,可能是5。

<强> 2。树设计模式。

当所有元素相等(如果不相似)时,这些适用 通常是相同的类或接口,通常是很多项目。

这些不适用于您的情况,但是, 我不得不提一下,要更好地应用。

通常,使用树/层次集合类。 它可以是通用/模板树集合的子类, 或者基础tre集合的子类, 这个目的是由具有特定成员的子类替换。

public class FileSystemRootClass {

  public bool AmISystemRoot() {
    // more
  }

  public bool AmIAFolder() {
    // more
  }

  public bool AmIAFile() {
    // more
  }

  public void addSystemRoot(string FileSystemName) {
    // more
  }

  public void addFolder(string FileSystemName) {
    // more
  }

  public void addFile(string FileSystemName) {
    // more
  }

  // members here
}

第3。混合动力。

这两个是前两个的组合, 当有很多相关物品时使用它, 它更复杂,可能使用或不使用工厂&amp;抽象工厂模式, 其更常见的例子是视觉控制和小部件库。

import java.util.*;
public class WidgetsExample {
  public static void main(String[] args) {
    TreeSet <Widget> FormControls = new TreeSet<Widget>();

    TextBoxWidget TextBox = new TextBoxWidget();
    FormControls.add(TextBoxWidget);

    ListBoxWidget ListBox = new ListBoxWidget();
    FormControls.add(TextBoxWidget);

    ButtonWidget Button = new ButtonWidget();
    FormControls.add(Button);
} // class WidgetsExample

您可能会注意到我没有使用“工厂模式”&amp; “抽象工厂”, 由于需要更多代码。

祝你好运。

答案 2 :(得分:0)

我会尝试这样的事情:

interface StoryElement<SE extends SubElements>{
    List<SE> getContents()
}

class Story<T extends StoryElement>


class Book implements StoryElement<Chapter> ...
class Chapter implements StoryElement<Section> ...
class Section implements StoryElement<Text>
class Text implements StoryElement<Text> {... // ugly hack, don't know of a clean way to end the recursion with Java Generics

现在你可以拥有书籍故事或文字故事。

警告:在尝试复杂的事情时,Java Generics往往会黯然失色。如有疑问,请忘记泛型和演员。

答案 3 :(得分:0)

感谢所有的海报。这是我正在进行中的解决方案:

public abstract class Element
{
    public ElementSet getContent() { return content; }

    // Overrides of setContent() check to see if the content type is appropriate
    // using instanceof.
    public void setContent( ElementSet content )
    {
        this.content = content;
    }


    private ElementSet content;
}

public abstract class ElementSet
{
    protected final void addElement( Element e )
    {
         elements.add(e);
    }

    private final List<Element> elements = new ArrayList<Element>();
}

public class BookSet extends ElementSet
{
    // Typesafe interface. 
    public void addBook( Book book ) { super.addElement( book ); }
}

public class ChapterSet extends ElementSet { /* similar to BookSet */ }

public class SectionSet extends ElementSet { /* similar to BookSet */ }

public class Book extends Element
{
    @Override
    public void setContent( ElementSet content )
    {
        if ( !(content instanceof ChapterSet) && !(content instanceof SectionSet) )
        {
            throw new RuntimeException();
        }
        super.setContent( content );
    }

    public boolean addChapter( Chapter chapter )
    {
        ElementSet content = getContent();
        if ( content == null )
        {
            content = new ChapterSet();
            setContent( content );
        }
        else if ( !(content instanceof ChapterSet) )
        {
            // Structure is wrong.
            return false;
        }

        ChapterSet chapters = (ChapterSet)content;
        chapters.addChapter( chapter);

        return true;        
    }

    public boolean addSection( Section section )
    {
        ElementSet content = getContent();
        if ( content == null )
        {
            content = new SectionSet();
            super.setContent( content );
        }
        else if ( !(content instanceof SectionSet) )
        {
            // Structure is wrong.
            return false;
        }

        SectionSet sections = (SectionSet)content;
        sections.addSection( section );

        return true;                
    }
}

public class Chapter extends Element
{
    @Override
    public void setContent( ElementSet content )
    {
        if ( !(content instanceof SectionSet) )
        {
            throw new RuntimeException();
        }
        super.setContent( content );
    }

    public boolean addSection( Section section )
    {
        ElementSet content = getContent();
        if ( content == null )
        {
            content = new SectionSet();
            super.setContent( content );
        }
        else if ( !(content instanceof SectionSet) )
        {
            // Structure is wrong.
            return false;
        }

        SectionSet sections = (SectionSet)content;
        sections.addSection( section );

        return true;                
    }
}

我尝试使用泛型来实现相同目的,但由于需要反映容器的参数化类型,它看起来相当丑陋。