不确定我应该在这里使用接口,但是什么呢?

发布于 2024-11-09 19:32:04 字数 1246 浏览 1 评论 0原文

我有多个 *.ascx 控件。每一个都代表一个Microsoft Chart Control,但图表类型不同。这些图表都公开了多个具有相同功能的方法,因为它们都实现了 IChartControl 接口。我讨厌这样一个事实:在某些情况下,每个图表的实现都是相同的——代码是复制/粘贴的。

我选择让它们实现 IChartControl 接口,因为我找不到允许这些图表控件使用相同功能的合理复杂性的解决方案。事实证明,继承“Chart”基类极其复杂,因为您无法真正继承控件背后的 HTML 标记。

我想做的是:

其中一个类的签名是这样的:

public partial class HistoricalLineGraph : System.Web.UI.UserControl, IChartControl

我想创建一个继承自 System.Web.UI.UserControl 的新类。此类将实现 IChartControl 接口。通过这种方式,我可以为我想要定义的方法提供一个基本实现,但我遇到了障碍。看一下下面的方法,我想将其抽象到更高的级别,以便代码由我的所有图表类继承:

public void LoadData(string data)
{
    if (!string.IsNullOrEmpty(data))
    {
        byte[] dataAsArray = System.Text.Encoding.UTF8.GetBytes(data);
        MemoryStream stream = new MemoryStream(dataAsArray);
        Chart1.Serializer.Load(stream);
    }
}

HistoricalLineGraph 和 System.Web.UI.UserControl 之间的中间人类将没有以下概念:对象“Chart1”,如 HistoricalLineGraph 中定义的那样。

我缺少的这个问题有明显的解决方案吗?

编辑:我希望能够使用 IChartControl 接口中定义的属性执行某些操作。如果我将“图表”控件作为参数传递给上述函数,下面的解决方案是什么?

public string Title 
{
    get { return Chart1.Titles[1].Visible ? Chart1.Titles[1].Text : Chart1.Titles[0].Text; }
    set { Chart1.Titles[0].Text = value; }
}

I have multiple *.ascx controls. Each one represents a Microsoft Chart Control, but different types of charts. These charts all expose several methods with identical functionality because they all implement an IChartControl interface. I hate the fact that there are cases where each chart's implementation is identical -- the code is copy/pasted.

I chose to have them implement an IChartControl interface because I could not find a solution of reasonable complexity that would allow these chart controls to utilize the same functions. Inheriting a base 'Chart' class proved exceedingly complicated in that you can't really inherit the HTML markup behind the controls.

What I would like to do is the following:

The signature for one of the classes is this:

public partial class HistoricalLineGraph : System.Web.UI.UserControl, IChartControl

I would like to create a new class which inherits from System.Web.UI.UserControl. This class would implement the IChartControl interface. In this way I could provide a base implementation for the methods I want to define, but I run into a snag. Look at the following method that I would like to abstract out to a higher level so that the code is inherited by all of my charting classes:

public void LoadData(string data)
{
    if (!string.IsNullOrEmpty(data))
    {
        byte[] dataAsArray = System.Text.Encoding.UTF8.GetBytes(data);
        MemoryStream stream = new MemoryStream(dataAsArray);
        Chart1.Serializer.Load(stream);
    }
}

A middle-man class between HistoricalLineGraph and System.Web.UI.UserControl will have no concept of the object 'Chart1' as it is defined in HistoricalLineGraph.

Is there an obvious solution to this problem that I am missing?

EDIT: I would like to be able to do something with properties defined in the IChartControl interface. If I am passing a 'Chart' control as a parameter to the above function, what would a solution be to the below?

public string Title 
{
    get { return Chart1.Titles[1].Visible ? Chart1.Titles[1].Text : Chart1.Titles[0].Text; }
    set { Chart1.Titles[0].Text = value; }
}

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

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

发布评论

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

评论(3

絕版丫頭 2024-11-16 19:32:04

我们可以做的第一个观察是,如果您可以让所有图表类都继承自 BaseChartControl,那么您根本不再需要 IChartControl!仅当两个不同的类实现接口但它们不继承自可以“容纳”公共 API 的公共基类时,才严格需要接口。

另一方面,接口是统一从您无法控制的公共基础派生的多个类的公共行为的完美方式。如果您继续让图表类直接从 UserControl 派生,就会出现这种情况。

现在,无论您选择哪种方法(有接口或无接口),您仍然会遇到以 LoadData 为例的抽象问题。让我们努力吧。

抽象是一项艰巨的任务,因为您试图分离出公共部分并保留使类不同的部分。在不造成混乱的情况下完成此操作是任何设计类层次结构的人面临的主要设计挑战之一。

但特别是对于 LoadData,它唯一不常见行为的部分是对 Chart1 本身的引用。在我看来,您至少有三个选择:

  • 在基类中包含 Chart1,现在也很常见
  • Chart1 作为附加参数提供给 LoadData
  • 更改 LoadData,使其返回一个 MemoryStream 并让调用者对其进行序列化。

重点是区分常见行为和独特行为。决定如何干净利落地完成这项工作需要付出努力,当您最终决定最好的方法时,这既是一种挑战,也是一种回报。

The first observation we can make is that if you can have all your chart classes inherit from BaseChartControl, then you no longer need IChartControl at all! An interface is only strictly needed when two different classes implement the interface but they don't inherit from a common base class that can "house" the common API.

On the other hand, an interface is a perfect way to unify the common behaviors of several classes that derive from a common base that you have no control over. This would be the case if you continue to have your chart classes derive directly from UserControl.

Now, whichever approach you choose (interface or no interface), you still have your abstraction problem that you've used LoadData as an example of. Let's work on that.

Abstraction is a difficult task because you are trying to separate out the common parts and leave in the parts that make the classes different. Doing this without creating a mess is one of the major design challenges that anyone who is designing a class hierarchy faces.

But specifically for LoadData, the only part of it that isn't common behavior is the reference to Chart1 itself. As I see it you have at least three choices:

  • Include Chart1 in the base class and now it is common too
  • Supply Chart1 as an additional parameter to LoadData
  • Change LoadData so that it returns a MemoryStream and let the caller serialize it

The point is the separation of common and unique behavior. It takes work to decide how to do this cleanly and that makes it both a challenge and rewarding when you finally decide on the best way to do it.

智商已欠费 2024-11-16 19:32:04

如果您有一个接口的多个实现,并且所有实现都以相同的方式实现其方法之一,那么您可以使用提取方法重构。将重复的代码放入某个静态方法中,然后从接口实现中调用它。

如果重复更普遍并且跨越整个接口,那么您最好编写一个辅助类来实现该接口。您可以删除重复的代码并将其委托给帮助器类的实例。

后一个想法类似于您在继承中尝试的想法,但它是组合、委托的一种形式。这是必需的,因为 C# 只支持实现的单一继承。

If you have multiple implementations of an interface that all implement one of its methods in identical fashion then you can use the extract method refactoring. Put the duplicated code into a static method somewhere and call it from your interface implementations.

If the duplication is more pervasive and spans the entire interface then you would be better off writing a helper class to implement that interface. You can strip out the duplicated code and delegate to an instance of the helper class.

This latter idea is similar to what you tried with inheritance, but instead is a form of composition, delegation. This is needed because C# only supports single inheritance for implementations.

七婞 2024-11-16 19:32:04

您当然可以做几件事。最简单的是类继承和抽象函数指针(委托、事件、虚函数)来处理类特定的事情。

举个例子会有帮助。在您的 BaseClass 中...

public event Action<BaseClass, string> DataLoaded;

protected virual void DoLoadDataLoaded(string data)
{
    var e = DataLoaded;
    if(null != e)
        e(this, data);
}

protected virtual void DoLoadStream(MemoryStream stream)
{
    Chart1.Serializer.Load(stream);
}

public void LoadData(string data)
{
    if (!string.IsNullOrEmpty(data))
    {
        byte[] dataAsArray = System.Text.Encoding.UTF8.GetBytes(data);
        MemoryStream stream = new MemoryStream(dataAsArray);
        DoLoadStream(stream);
        DoLoadDataLoaded(data);
    }
}

现在,在您的子类中,您可以重载两个 Do 虚函数并修改它们,以便它们反映子类。您还可以订阅该活动并在那里做一些专业的事情。

例如,您发现 Chart1 对于子类没有用处。因此,您可以像这样重写 DoLoadStream:

protected override void DoLoadStream(MemoryStream stream)
{
    // do nothing or do something else.  Base class's DoLoadStream never executes!
}

还有另一种选择,但我发现它更适合类库 - 如果您决定将代码重构为控件都依赖的单独类,您可以在方法中传递委托来电。

例如:

public void LoadData(string data, Action<BaseClass, string, MemoryStream> handler)
{
    if (!string.IsNullOrEmpty(data))
    {
        byte[] dataAsArray = System.Text.Encoding.UTF8.GetBytes(data);
        MemoryStream stream = new MemoryStream(dataAsArray);
        handler(this, data, stream); // do something with it!
        DoLoadStream(stream);  // can still keep/use these if you want
        DoLoadDataLoaded(data);
    }
}

我希望这有帮助!

You can of course do several things. The easiest is class inheritance and abstract function pointers (delegates, events, virtual functions) to handle class specific things.

An example would help. In your BaseClass ...

public event Action<BaseClass, string> DataLoaded;

protected virual void DoLoadDataLoaded(string data)
{
    var e = DataLoaded;
    if(null != e)
        e(this, data);
}

protected virtual void DoLoadStream(MemoryStream stream)
{
    Chart1.Serializer.Load(stream);
}

public void LoadData(string data)
{
    if (!string.IsNullOrEmpty(data))
    {
        byte[] dataAsArray = System.Text.Encoding.UTF8.GetBytes(data);
        MemoryStream stream = new MemoryStream(dataAsArray);
        DoLoadStream(stream);
        DoLoadDataLoaded(data);
    }
}

Now in your child class you can overload both of the Do virtual functions and modify them so that they reflect the child. You can also subscribe to the event and do specialty stuff there as well.

For example, you find that Chart1 has no use for a child class. So you override DoLoadStream like so:

protected override void DoLoadStream(MemoryStream stream)
{
    // do nothing or do something else.  Base class's DoLoadStream never executes!
}

There is another option, but I find it better suited for class libraries - and in case you decide to instead refactor your code out as a separate class that the controls all rely on, you can pass delegates in method calls.

For example:

public void LoadData(string data, Action<BaseClass, string, MemoryStream> handler)
{
    if (!string.IsNullOrEmpty(data))
    {
        byte[] dataAsArray = System.Text.Encoding.UTF8.GetBytes(data);
        MemoryStream stream = new MemoryStream(dataAsArray);
        handler(this, data, stream); // do something with it!
        DoLoadStream(stream);  // can still keep/use these if you want
        DoLoadDataLoaded(data);
    }
}

I hope this helps!

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