Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/307.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 针对此问题的适当设计/设计模式?_C#_.net_Design Patterns_Inheritance - Fatal编程技术网

C# 针对此问题的适当设计/设计模式?

C# 针对此问题的适当设计/设计模式?,c#,.net,design-patterns,inheritance,C#,.net,Design Patterns,Inheritance,我以前发过帖子,但我想它太冗长和不相关了。我的问题也是这样。第二个链接中的一张海报说,答案(为什么你不能做下面的代码)是设计问题,特别是“继承的错误使用”。因此,我想再次与StackOverflow的专家一起检查这个问题,看看这是否真的是一个“坏继承”的问题——但更重要的是,如何修复设计 像海报一样,我也对工厂方法以及如何应用它感到困惑。工厂方法似乎适用于多个具体类,这些类与抽象基类具有完全相同的实现,不添加自己的属性。但是,正如您将在下面看到的,我的具体类构建在抽象基类的基础上,添加额外的属性

我以前发过帖子,但我想它太冗长和不相关了。我的问题也是这样。第二个链接中的一张海报说,答案(为什么你不能做下面的代码)是设计问题,特别是“继承的错误使用”。因此,我想再次与StackOverflow的专家一起检查这个问题,看看这是否真的是一个“坏继承”的问题——但更重要的是,如何修复设计

像海报一样,我也对工厂方法以及如何应用它感到困惑。工厂方法似乎适用于多个具体类,这些类与抽象基类具有完全相同的实现,不添加自己的属性。但是,正如您将在下面看到的,我的具体类构建在抽象基类的基础上,添加额外的属性

我们构建的基础类:

public abstract class FlatScreenTV
{
     public string Size { get; set; }
     public string ScreenType { get; set; }
}
public class PhillipsFlatScreenTV : FlatScreenTV
{
     // Specific to Phillips TVs. Controls the backlight intensity of the LCD screen.
     public double BackLightIntensity { get; set; }
}

public class SamsungFlatScreenTV : FlatScreenTV
{
     // Specific to Samsung TVs. Controls the time until the TV automatically turns off.
     public int AutoShutdownTime { get; set; }
}
扩展类示例:

public abstract class FlatScreenTV
{
     public string Size { get; set; }
     public string ScreenType { get; set; }
}
public class PhillipsFlatScreenTV : FlatScreenTV
{
     // Specific to Phillips TVs. Controls the backlight intensity of the LCD screen.
     public double BackLightIntensity { get; set; }
}

public class SamsungFlatScreenTV : FlatScreenTV
{
     // Specific to Samsung TVs. Controls the time until the TV automatically turns off.
     public int AutoShutdownTime { get; set; }
}
比如说,更多品牌的平板电视有更多的扩展类。然后,假设我们将它们全部粘贴到一个通用列表中:

public static void Main()
{
     List<FlatScreenTV> tvList = new List<FlatScreenTV>();

     tvList.Add(new PhillipsFlatScreenTV());
     tvList.Add(new SamsungFlatScreenTV());
     tvList.Add(new SharpFlatScreenTV());
     tvList.Add(new VizioFlatScreenTV());

     FlatScreenTV tv = tvList[9]; // Randomly get one TV out of our huge list
}
publicstaticvoidmain()
{
List tvList=新列表();
添加(新屏幕电视());
添加(新SamsungFlatScreenTV());
添加(新的SharpFlatScreenTV());
添加(新的VizioFlatScreenTV());
FlatScreenTV=tvList[9];//从我们庞大的列表中随机抽取一台电视
}
问题:

public abstract class FlatScreenTV
{
     public string Size { get; set; }
     public string ScreenType { get; set; }
}
public class PhillipsFlatScreenTV : FlatScreenTV
{
     // Specific to Phillips TVs. Controls the backlight intensity of the LCD screen.
     public double BackLightIntensity { get; set; }
}

public class SamsungFlatScreenTV : FlatScreenTV
{
     // Specific to Samsung TVs. Controls the time until the TV automatically turns off.
     public int AutoShutdownTime { get; set; }
}
我想访问此变量所属的任何“原始”品牌电视的特定属性。我知道这个品牌,因为如果我调用
tv.GetType()
,它会返回正确的“原始”类型,而不是
FlatScreenTV
。但是我需要能够将
tv
FlatScreenTV
转换回其原始类型,以便能够访问每个品牌的平板电视的特定属性

问题#1:如果没有临时的黑客和巨大的if-else链来粗暴地猜测“原始”类型,我如何能够正确地动态地投射它?

浏览类似的设计问题后,大多数答案是:你不能。有人说要看工厂模式,也有人说要用接口修改设计,但我不知道如何使用两者来解决这个问题

问题2:那么,我应该如何设计这些类,以便在上面的上下文中访问原始类型的特定属性?


问题#3:这真的是糟糕的继承吗?将是最好的方式

我可以提供一个部分答案:

首先阅读利斯科夫的替代原理

其次,您正在创建从FlatScreenTV继承的对象,但显然没有任何目的,因为您希望通过它们的子类型(SpecificTVType)而不是超类型(FlatScreenTV)引用它们-这是继承的一个坏用法,因为它没有使用继承lol

如果您的代码希望访问特定于给定类型的属性,那么您确实希望将此代码封装在该类型中。否则,每次添加新电视类型时,处理电视列表的所有代码都需要更新以反映这一点

因此,您应该在FlatScreenTV上包含一个执行x的方法,并根据需要在TV中重写该方法

因此,基本上在上面的Main方法中,不要认为我想处理TVTypeX,而是应该始终引用basetype,让继承和方法重写处理实际处理的子类型的特定行为

代码

  public abstract class FlatScreenTV
  {
      public virtual void SetOptimumDisplay()
      {
         //do nothing - base class has no implementation here
      }
  }


  public class PhilipsWD20TV
  {
      public int BackLightIntensity {get;set;}

      public override void SetOptimumDisplay()
      {
          //Do Something that uses BackLightIntensity
      }

  }
您的设计违反了“”。换句话说,处理FlatScreenTV列表中项目的代码不应该知道或关心派生类型是什么

假设您的代码需要创建自定义远程控制GUI。只需知道每个电视的名称和属性类型就可以自动生成UI。在这种情况下,您可以执行以下操作以从基类公开自定义属性:

public abstract class FlatScreenTV
{
    public FlatScreenTV()
    {
        CustomProperties = new Dictionary<string,object>();
    }

    public Dictionary<string,object> CustomProperties { get; private set; }
    public string Size { get; set; }
    public string ScreenType { get; set; }
}

public class PhillipsFlatScreenTV : FlatScreenTV
{
    public PhillipsFlatScreenTV()
    {
        BackLightIntensity = 0;
    }

    // Specific to Phillips TVs. Controls the backlight intensity of the LCD screen.
    public double BackLightIntensity 
    { 
        get { return (double)CustomProperties["BackLightIntensity"]; }
        set { CustomProperties["BackLightIntensity"] = value; }
    }
}

public class SamsungFlatScreenTV : FlatScreenTV
{
    public SamsungFlatScreenTV()
    {
        AutoShutdownTime = 0;
    }

    // Specific to Samsung TVs. Controls the time until the TV automatically turns off.
    public int AutoShutdownTime 
    {
        get { return (int)CustomProperties["AutoShutdownTime"]; }
        set { CustomProperties["AutoShutdownTime"] = value; }
    }
}
这将扫描您的插件,并找到一个知道如何为您传入的特定类型的FlatScreenTV构建UI的插件。这意味着,对于您添加的每个新FlatScreenTV,您还需要创建一个知道如何制作其远程控制GUI的插件

“工厂方法适用于多个具体类,这些类与抽象基类[interface]具有完全相同的实现,并且不添加自己的属性。”

不,说得更实际一些,而不是理论上的,工厂方法可以为您提供具体类的对象,其中具体类必须有一些通用方法和接口,但也有一些附加的特定属性

有时我使用的方法每次调用时都会创建相同的类对象,我需要多次调用它,有时我使用的方法会创建几个不同的类对象,这可能会让人困惑,可能是另一个问题

此外,在使用工厂模式时,您对切换语句的进一步评论(带有许多选项),通常为具体类/具体对象提供标识符。这可以是字符串、整数、特殊类型id或枚举类型


您可以改为使用整数/枚举ID,并使用集合查找具体类。

您仍然可以利用工厂。工厂的目的是把制造各种电视机的所有重担放在一个地方。如果直截了当地说“一个工厂是为多个具体类设计的,这些具体类具有与抽象基类完全相同的实现”,那么就忘记了多态性

没有法律规定不能使用工厂模式,因为子类声明唯一的属性和方法。但是,使用多态性越多,工厂模式就越有意义。同样作为一个总的指导原则,IMHO认为,构建过程中必须考虑的复杂性越大