【问题标题】:Does this design need interfacing?这个设计需要接口吗?
【发布时间】:2014-11-01 03:25:13
【问题描述】:

我正在为邻居做一个小应用程序,目前对设计很困惑。开始看起来很简单,但现在我被卡住了。我理解继承,因此,

  • 所有礼物都必须包装好。
  • Gift 是一个抽象类。
  • 包装样式是一个抽象类。
  • 生日礼物继承自 Gift。
  • 折纸样式继承自环绕样式。

场景:用户向商店赠送礼物,并附上其身份(例如为家人生日,为同事生日)。所以有一份生日礼物是送给同事的,这与送给家人的礼物不同。用户指定他们喜欢使用的包装样式。 (例如折纸、西式)在这种情况下,假设给同事的生日礼物必须使用折纸下的生日样式进行包装。

但是如何将礼物与包装联系起来呢?起初,包装对我来说就像一个界面。所以礼物实现了它。但是包装每个主要包装样式都有子包装样式的事实使得它变得复杂。同事的礼物对象生日怎么知道要实现哪个接口呢?好像生日、婚礼也应该是抽象类,因为只有第 3 代礼品类是具体类。

我怎样才能让这个设计既有意义又易于编码,并在以后有修改/改进的空间?

【问题讨论】:

  • 我认为在设计复杂的类层次结构之前,您需要更清楚地指定域是什么以及您的程序试图解决什么问题。你想建模什么概念?从有限的描述来看,听起来您试图将太多概念放入类层次结构中。例如,朋友的结婚礼物是一种礼物吗?或者,礼物是否有场合(例如婚礼、生日等)、关系(例如家人、朋友、同事),以及包装样式。请注意这些名词如何成为它们自己的类型。首先列出所有概念及其关系。
  • @mike z Gift 有不同的类型礼物。这些儿童礼物中的每一个都有子类型。生日礼物永远不能和毕业礼物一样。但是由于方形和圆形继承自形状类,我的礼物继承自礼物类。我有一个困惑,我应该写 12 种不同类型的具体类(生日家庭、生日同事、婚礼朋友、婚礼家庭、毕业朋友、毕业家庭等)继承自礼物抽象类,因为每个人都没有自己独特的属性和方法?这根本没有任何意义:(
  • 如果生日礼物永远不能和毕业礼物一样,那为什么还要继承同一个基类呢?您的模型中子类型的激增是为什么我建议不要为每个场合/关系配对制作礼物子类型,而是让它们成为礼物对象的属性。该想法的适用性取决于该程序的设计目的。正如一些人已经提出的,礼物不是一种包装方式,而是一种包装方式。

标签: c# oop inheritance interface abstract-class


【解决方案1】:

为了将每个礼物与包装相关联,您确实想要实现包装。这是行不通的。相反,将包装作为一个变量包含在 Gift 中。

您似乎有方法使用抽象类(本质上是部分实现):

public abstract class Gift
{
    public IWrappingStyle wrapping { get; private set; }

    public Gift(IWrappingStyle wrapping)
    {
        this.wrapping = wrapping;
    }

    public void Unwrap()
    {
        // code common to all gifts for unwrapping
        // ...
    }
}

public interface IWrappingStyle
{

}

然后您可以继续将第二级类型表示为 Wrapping Style 的接口和用于礼物的抽象类,并将类表示为您需要的其余类型的最低级别。添加一个新类就像添加一个新类一样简单。

然后使用,你可以这样做:

Gift g = new GraduationSchoolGift(new OrigamiBirthdayWrapping());

别忘了使用文件夹!


但是,除非您对不同的包装等有一些特定的不同行为,否则我认为您可以使用更简单的布局:

public class Gift
{
    public String GiftType { get; private set; }
    public String WrappingStyle { get; private set; }

    public Gift(String giftType, String wrappingStyle)
    {
        this.GiftType = giftType;
        this.WrappingStyle = wrappingStyle;
    }
}

然后,如果您(再次)将文件组织在这样的文件夹中:

这里是各自的文件(命名空间很重要):

礼物种类:

namespace GiftWrapping.GiftTypes
{
    public class Birthday
    {
        public static const String FIFTH_BIRTHDAY = "Birthday Fifth";
        public static const String TENTH_BIRTHDAY = "Birthday Tenth";
    }
}

namespace GiftWrapping.GiftTypes
{
    public class Wedding
    {
        public static const String FAMILY = "Wedding Family";
        public static const String FRIENDS = "Wedding Friends";
    }
}

包装样式:

namespace GiftWrapping.WrappingStyles
{
    public class Origami
    {
        public static const String BIRTHDAY = "Origami Birthday";
        public static const String WEDDING = "Origami Wedding";
    }
}

namespace GiftWrapping.WrappingStyles
{
    public class Western
    {
        public static const String SCHOOL = "Western School";
        public static const String WEDDING = "Western Wedding";
        public static const String UNIVERSITY = "Western University";
    }
}

现在用法变成:

Gift g = new Gift(GiftTypes.Wedding.FAMILY,
                  WrappingStyles.Origami.BIRTHDAY);

【讨论】:

  • @aspiring 我正在对我的帖子进行修改。
  • @aspiring 我已经更新了我的答案。您提供的关于您的要求的信息很少,所以我只是在使用我所拥有的。
  • +1 Adam 我正在看你的解释。你可以在 SE 上聊天吗? (我可能还有更多的澄清要做)=)
  • @aspiring 当然。哪个房间?我以前从未使用过聊天。
  • 我刚邀请你参加聊天。您可能收到了邀请。
【解决方案2】:

其实写一个接口是一个很好的习惯。当您编写单元测试时,它们可以真正帮助您,并且是 SOLID 的基础。所以你可以尝试这样的事情:

  public interface IGift
    {
        public void Wrap(IWrappingStyle wrappingStyle)
        {

        }
    }

    public interface IWrappingStyle
    {

    }

有了这种对象依赖,你可以使用类似策略模式的东西来根据类的类型来处理包装样式。

【讨论】:

    【解决方案3】:

    Gift 和 wrapping 是独立的实体,使用依赖注入。

    Class wrapping{
        protected $gift;
    
        public __construct( $gift ){
            $this->gift = $gift
        }
    
        public getGift(){
           return $this->gift;
        }
    
    }
    
    Class gift{
    
    
    }
    
    
    $g = new gift()
    
    $w = new wrapper($g);
    

    这样,您无需为每个新礼物更换包装,或为每个新包装更换礼物。无论您是界面礼物,还是使其抽象取决于功能。包装也是如此。不要在“代码”级别混合它们,即使上图显示它们是单独的对象。

    就我个人而言,我要么接口礼物,要么为它创建一个基础抽象类。然后,您可以依靠在包装内包含您需要的代码的未来礼物。

    哈刚刚意识到这是 C,我是怎么做到的。无论如何,即使不是 PHP,想法也是一样的。

    干杯。

    【讨论】:

    • Gift 作为一个抽象类是显而易见的。但由于礼品类型的不同,我不明白它如何与包装联系起来。 “你是接口礼物还是抽象取决于功能”是什么意思?
    • 如果所有的礼物共享共同的代码,则基础是抽象的。否则,您将不得不在所有未来的礼物中重新编码该通用功能。如果礼物的代码可以有很大不同,但你只需要依赖方法名称等,那么它就是一个接口。
    • 当然抽象性写在我的问题“gift class is abstract”,“giftwrapper is abstract”中!但是我无法实施您的建议。您能否通过扩展示例解释设计或引导我举一个示例?我的主要问题是礼物如何决定适应哪种风格?
    • 我不会创建礼品包装纸。那是我的观点。不要混合两者的代码。一个包含另一个。它们不是同一件事。汽车不是车库,你不做一个车库类。你把车放在车库里,可以这么说。例如,如果您想在同一个包装纸中包装 2 件礼物怎么办。如果你混合他们的代码是不可能的。
    • @ArtisiticPhoenix 我不确定您使用的是什么语言,但这看起来不像 C#。
    猜你喜欢
    • 1970-01-01
    • 2010-09-30
    • 2015-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-01
    • 1970-01-01
    • 2021-05-30
    相关资源
    最近更新 更多