Ich habe aus dem Buch "C# 3.0 Entwurfsmuster" folgendes Beispiel für das Dekorierer-Muster bestehend aus einer Klasse "Photo", zwei Dekorierer-Klassen und eine zum Ausführen
public class Photo:Form
{
Image img;
public Photo()
{
img = new Bitmap("Bild.JPG");
this.Text = "Bild";
this.Paint += Drawer;
this.Size = img.Size;
}
public virtual void Drawer(Object soure, PaintEventArgs e)
{
e.Graphics.DrawImage(img, 10, 20);
}
}
public class BorderedPhoto:Photo
{
Photo photo;
Color color;
public BorderedPhoto(Photo p, Color c)
{
photo = p;
color = c;
}
public override void Drawer(Object source, PaintEventArgs e)
{
photo.Drawer(source, e);
e.Graphics.DrawRectangle(new Pen(color,10), 25,15,215,225);
}
}
public class TaggedPhoto:Photo
{
Photo photo;
string tag;
int number;
static int count;
List <string> tags = new List <string>();
public TaggedPhoto(Photo p, string s)
{
photo = p;
tag = s;
tags.Add(s);
number = ++count;
}
public override void Drawer(Object source, PaintEventArgs e)
{
photo.Drawer(source, e);
e.Graphics.DrawString(tag,
new Font("Arial", 16),
new SolidBrush(Color.Green),
new PointF(80,100+number*20));
}
public string ListTaggedPhotos()
{
string s = "Tags: ";
foreach (string t in tags) {
s += t+" ";
}
return s;
}
}
class Program
{
public static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Photo photo;
TaggedPhoto foodTaggedPhoto, colorTaggedPhoto, tag;
BorderedPhoto composition;
photo = new Photo();
Application.Run(photo);
foodTaggedPhoto = new TaggedPhoto(photo, "Food");
colorTaggedPhoto = new TaggedPhoto(foodTaggedPhoto,"Yellow");
composition = new BorderedPhoto(colorTaggedPhoto, Color.Blue);
Application.Run(composition);
Console.WriteLine(colorTaggedPhoto.ListTaggedPhotos());
}
}
Soweit alles in Ordnung. Nun soll laut Aufgabenstellung die Methode "Draw" der Klasse Photo als nicht überschreibbar deklariert werden. Das habe ich getan und des Weiteren ein Interface IPHoto zur Verfügung gestellt, das von Photo und den beiden Dekorierern implementiert wird. Die Instanzen "photo" in den beiden Dekoriererklassen sind dann vom Typ IPhoto und der erste Parameter in deren Konstruktoren ebenfalls.
Nun habe ich allerdings ein Problem mit der Methode "Run", die als Parameter nur eine Instanz der Klasse "Form" oder einer davon abgeleiteten Klasse akzeptiert.
Wie muss ich meine Dekorierer instanzieren, um sie der "Run"-Methode übergeben zu können?
Über das Buch C# 3.0 Entwurfsmuster gibt es kontroverse Ansichten, siehe auch Nicht empfehlenswert: C# 3.0 Entwurfsmuster
Ich finde das Beispiel für Decorator Pattern auch nicht wirklich gelungen.
Aber Deine Klasse Photo
(besser wäre PhotoForm
) erbt ja von Form
.
Application.Run
nimmt alle Klassen, die von Form
erben, an.
Dahingehend geht problemlos
Application.Run(new Photo());
Das ist auch das Standardverhalten. Jede Form - auch das Standardtemplate Form1
von Visual Studio beim Anlegen eines Windows Form-Projekts - erbt von Form
.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Ich glaube mein Anliegen ist nicht ganz deutlich geworden.
Die Aufgabe lautet:
Nehmen Sie an, dass in der Klasse _Photo _die Methode Drawer normal (und nicht virtuell) ist und deshalb nicht überschrieben werden kann. Schreiben Sie das Beispiel so um, dass es unter dieser Bedingung funktioniert. (Hinweis: Verwenden Sie ein Interface).
Und folgendes habe ich gemacht
Erstellen des Interface IPhoto
public interface IPhoto
{
void Drawer(object sender, PaintEventArgs e);
}
Klasse _Photo _erbt weiterhin von Form und implementiert IPhoto; die Methode Draw ist nicht mehr überschreibbar.
public class Photo:Form,IPhoto
{
Image img;
public Photo()
{
img = new Bitmap("Bild.JPG");
this.Text = "Bild";
this.Paint += Drawer;
this.Size = img.Size;
}
public void Drawer(Object soure, PaintEventArgs e)
{
e.Graphics.DrawImage(img, 10, 20);
}
}
Veränderte Klasse TaggedPhoto
public class TaggedPhoto:IPhoto
{
IPhoto photo;
string tag;
int number;
static int count;
List <string> tags = new List <string>();
public TaggedPhoto(IPhoto p, string s)
{
photo = p;
tag = s;
tags.Add(s);
number = ++count;
}
public void Drawer(Object source, PaintEventArgs e)
{
photo.Drawer(source, e);
e.Graphics.DrawString(tag,
new Font("Arial", 16),
new SolidBrush(Color.Green),
new PointF(80,100+number*20));
}
public string ListTaggedPhotos()
{
string s = "Tags: ";
foreach (string t in tags) {
s += t+" ";
}
return s;
}
}
Veränderte Klasse BorderedPhoto
public class BorderedPhoto:IPhoto
{
IPhoto photo;
Color color;
public BorderedPhoto(IPhoto p, Color c)
{
photo = p;
color = c;
}
public void Drawer(Object source, PaintEventArgs e)
{
photo.Drawer(source, e);
e.Graphics.DrawRectangle(new Pen(color,10), 25,15,215,225);
}
}
Ausführende Klasse; den Code habe ich unverändert gelassen bis auf das Objekt photo, das nun vom Typ IPhoto ist. Die beiden Methodenaufrufe von Run führen dann zu Fehlern, weil keine Instanz von Form oder abgeleiteter Klasse übergeben wurde. Beim ersten Aufruf könnte man noch Casten, aber beim Zweiten weiß ich nicht.
Was muss ich verändern, damit die in der Aufgabenstellung genannte Bedingung erfüllt ist?
internal sealed class Program
{
/// <summary>
/// Program entry point.
/// </summary>
[STAThread]
private static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
IPhoto photo;
TaggedPhoto foodTaggedPhoto, colorTaggedPhoto, tag;
BorderedPhoto composition;
photo = new Photo();
Application.Run(photo);
foodTaggedPhoto = new TaggedPhoto(photo, "Food");
colorTaggedPhoto = new TaggedPhoto(foodTaggedPhoto,"Yellow");
composition = new BorderedPhoto(colorTaggedPhoto, Color.Blue);
Application.Run(composition);
Console.WriteLine(colorTaggedPhoto.ListTaggedPhotos());
}
}
Hi,
wie Abt schon geschrieben hat, muss eine Klasse, die über Application.Run aufgerufen wird, von Form erben. Das tut Deine Klasse BorderedPhoto aber nicht. Deswegen funktioniert es nicht.
Sein Problem ist, dass er nur das Interface IPhoto
übergibt.
IPhoto photo; // hat nichts mit Form zutun
photo = new Photo();
Application.Run(photo); // Will Form, bekommt aber IPhoto
IPhoto
hat aber nichts mit Form
zutun.
Photo
erbt von Form.
Korrekt(er) wäre an dieser Stelle auf das Interface zu verzichten.
Photo photo; // Erbt von Form
photo = new Photo();
Application.Run(photo); // Will Form und bekommt eine Klasse, die von Form erbt
Ich find das Beispiel für einen Decorator absolut nicht gut.
Eher Decorator Design Pattern .NET
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Das leuchtet mir alles ein, aber es ist nicht die Lösung des Problems/der Aufgabe. Ihr habt mir erklärt was falsch läuft. Das weiß ich aber bereits 🙂
Was muss ich denn konkret verändern, damit meine Dekoriererklassen funktionieren?
Ich finde das Beispiel auch nicht so gut, aber es muss doch zu lösen sein.
Beide Dekorierer müssen Form implementieren, sonst wird das nix. Wurde dir auch schon einige Male erklärt.
public class TaggedPhoto: Form, IPhoto
Der Grund dafür, und auch das wurde dir bereits erklärt, liegt darin, dass der Aufruf von Application.Run()
als Parameter eine Klasse erwartet, die Form
implementiert.
Das Decorator-Beispiel aus Head First: Design Patterns ist deinem übrigens um Längen überlegen. Plus, da das Buch für Java ist, lernt man sogar mehr, wenn man die Beispiele auf C# umsetzt (weil man nicht stur abtippen kann, sondern überlegen muss). Und ich würde wetten, dass die anderen Beispiele auch besser sind.
LaTino
Edit: anbei ein Vorschlag, wie das besser zu lösen ist. Die dekorierte Klasse sollte nicht von Forms erben, das ist nicht ihre Aufgabe. Stattdessen wird eine abstrakte Basisklasse für alle Dekorierer eingefuegt, die die Drawer-Methode überschreibbar implementiert oder als abstract vorgibt, und diese erbt von Form. So wird ein bisschen mehr ein Schuh draus. (Ich hatte ganz zum Schluss keine richtige Lust mehr, mich mit dem UML-Tool herumzuschlagen. Rechts unten die Klasse sollte eigtl. BorderedDrawer o.ä. heißen, halt ein konkreter Dekorierer.)
"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)
So hatte ich mir das mittlerweile auch überlegt, nur ohne die abstrakte Klasse.
Danke!