2013-08-19 50 views
6

Tôi đã nói 3 lớp, Động vật, Mèo & Chó.Có thể tạo một lớp dẫn xuất từ ​​một hàm tạo lớp cơ sở không?

// calling code 
var x = new Animal("Rex"); // would like this to return a dog type 
var x = new Animal("Mittens"); // would like this to return a cat type 

if(x.GetType() == typeof(Dog)) 
{ 
    x.Bark(); 
} 
else 
{ 
    x.Meow(); 
} 


class Animal 
{ 
    public Animal(string name) 
    { 
     // check against some list of dog names ... find rex 
     // return Animal of type Dog. 

     // if not... 

     // check against some list of cat names ... find mittens 
     // return Animal of type Cat. 
    } 
} 

Điều này có thể bằng cách nào đó không? Nếu không có cái gì đó tương tự tôi có thể làm gì?

+2

Không thực sự mà không hạn chế bản thân ... cơ sở đẳng cấp của bạn sẽ cần phải biết lớp con của nó, mà không phải là rất hữu ích. –

Trả lời

9

Điều bạn đang tìm kiếm là một 'hàm tạo ảo' (không phải là thuộc tính trong C#) hoặc mẫu Nhà máy.

class Animal 
{ 
    // Factory method 
    public static Animal Create(string name) 
    { 
     Animal animal = null; 
     ... // some logic based on 'name' 
      animal = new Zebra(); 

     return animal; 
    } 
} 

Phương pháp nhà máy cũng có thể được đặt trong một lớp (Nhà máy) khác. Điều đó mang lại tốt hơn tách, vv

5

số cơ bản sửa chữa đúng là sử dụng một phương pháp tĩnh mà có thể tạo ra một thể hiện của các loại quyền:

var x = Animal.ForName("Rex"); 
var x = Animal.ForName("Mittens"); 

... 

public abstract class Animal 
{ 
    public static Animal ForName(string name) 
    { 
     if (dogNames.Contains(name)) 
     { 
      return new Dog(name); 
     } 
     else 
     { 
      return new Cat(name); 
     } 
    } 
} 

Hoặc này có thể là một dụ phương pháp trong một AnimalFactory loại (hoặc bất kỳ thứ gì). Đó sẽ là một cách tiếp cận mở rộng hơn - nhà máy có thể thực hiện một giao diện, ví dụ, và có thể được tiêm vào lớp cần thiết để tạo ra các cá thể. Nó thực sự phụ thuộc vào bối cảnh mặc dù - đôi khi cách tiếp cận đó là quá mức cần thiết.

Về cơ bản, một new Foo(...) gọi luôn tạo ra một thể hiện của chính xácFoo. Trong khi phương thức tĩnh được khai báo với kiểu trả về là Foo có thể trả lại tham chiếu đến bất kỳ loại nào tương thích với Foo.

1

Không, tôi không nghĩ rằng nó có thể theo cách bạn muốn.

Bạn có thể tạo lớp tĩnh có phương thức trả về động vật dựa trên tên, ví dụ:

static Animal CreateAnimal(string name) 
{ 
    if(catList.Contains(name)) 
     return new Cat(name"); 
    else if(dogList.Contains(name)) 
     return new Dog(name); 

    return null; 
} 
0

Các câu trả lời khác cho thấy bạn cần sử dụng mẫu nhà máy nhưng tôi muốn cung cấp cho bạn một ví dụ "thực tế" về cách bạn thực hiện. Tôi đã làm chính xác những gì bạn đang làm, tuy nhiên tôi đã làm việc với EPL2 printer language. Khi tôi thấy X Tôi cần tạo một thể hiện của lớp Rectangle, khi tôi thấy A Tôi cần tạo một thể hiện của lớp Text.

(Tôi đã viết a long time ago vì vậy tôi chắc chắn một số điều tôi đã làm có thể được cải thiện).

public partial class Epl2CommandFactory 
{ 
    #region Singelton pattern 
    private static volatile Epl2CommandFactory m_instance; 
    private static object m_syncRoot = new object(); 

    public static Epl2CommandFactory Instance 
    { 
     get 
     { 
      if (m_instance == null) 
      { 
       lock (m_syncRoot) 
       { 
        if (m_instance == null) 
        { 
         m_instance = new Epl2CommandFactory(); 
        } 
       } 
      } 
      return m_instance; 
     } 
    } 
    #endregion 

    #region Constructor 
    private Epl2CommandFactory() 
    { 
     m_generalCommands = new Dictionary<string, Type>(); 
     Initialize(); 
    } 
    #endregion 

    #region Variables 
    private Dictionary<string, Type> m_generalCommands; 

    private Assembly m_asm; 
    #endregion 

    #region Helpers 
    private void Initialize() 
    { 
     Assembly asm = Assembly.GetAssembly(GetType()); 
     Type[] allTypes = asm.GetTypes(); 
     foreach (Type type in allTypes) 
     { 
      // Only scan classes that are not abstract 

      if (type.IsClass && !type.IsAbstract) 
      { 
       // If a class implements the IEpl2FactoryProduct interface, 

       // which allows retrieval of the product class key... 

       Type iEpl2FactoryProduct = type.GetInterface("IEpl2GeneralFactoryProduct"); 
       if (iEpl2FactoryProduct != null) 
       { 
        // Create a temporary instance of that class... 

        object inst = asm.CreateInstance(type.FullName); 

        if (inst != null) 
        { 
         // And generate the product classes key 

         IEpl2GeneralFactoryProduct keyDesc = (IEpl2GeneralFactoryProduct)inst; 
         string key = keyDesc.GetFactoryKey(); 
         m_generalCommands.Add(key, type); 
         inst = null; 
        } 
       } 
      } 
     } 
     m_asm = asm; 
    } 
    #endregion 

    #region Methods 
    public IEpl2Command CreateEpl2Command(string command) 
    { 
     if (command == null) 
      throw new NullReferenceException("Invalid command supplied, must be " + 
              "non-null."); 

     Type type; 
     if (!m_generalCommands.TryGetValue(command.Substring(0, 2), out type)) 
      m_generalCommands.TryGetValue(command.Substring(0, 1), out type); 
     if (type != default(Type)) 
     { 
      object inst = m_asm.CreateInstance(type.FullName, true, 
               BindingFlags.CreateInstance, 
       null, null, null, null); 

      if (inst == null) 
       throw new NullReferenceException("Null product instance. " + 
        "Unable to create necessary product class."); 

      IEpl2Command prod = (IEpl2Command)inst; 
      prod.CommandString = command; 
      return prod; 
     } 
     else 
     { 
      return null; 
     } 
    } 
    #endregion 
} 

Cách mã làm việc là tôi sử dụng singleton pattern để tạo ra một factory class để mọi người có thể gọi var command = Epl2CommandFactory.Instance.CreateEpl2Command("..."); chuyển trong chuỗi lệnh EPL2 và nó trả về một cá thể của lớp đại diện cho lớp cụ thể đó.

Trong khi khởi tạo, tôi sử dụng phản chiếu để tìm các lớp hỗ trợ giao diện IEpl2GeneralFactoryProduct, nếu lớp hỗ trợ giao diện, nhà máy lưu trữ một hoặc hai mã thư đại diện cho lệnh máy in trong từ điển các loại.

Khi bạn cố gắng để tạo ra các lệnh nhà máy nhìn lên lệnh in trong từ điển và tạo ra lớp đúng, nó sẽ vượt qua chuỗi lệnh đầy đủ trên cho rằng lớp học để chế biến tiếp.

Dưới đây là một bản sao của một lớp chỉ huy và cha mẹ của nó nếu bạn muốn nhìn thấy nó

Rectangle:

[XmlInclude(typeof(Rectangle))] 
public abstract partial class Epl2CommandBase { } 

/// <summary> 
/// Use this command to draw a box shape. 
/// </summary> 
public class Rectangle : DrawableItemBase, IEpl2GeneralFactoryProduct 
{ 
    #region Constructors 
    public Rectangle() : base() { } 
    public Rectangle(Point startingLocation, int horozontalEndPosition, int verticalEndPosition) 
     : base(startingLocation) 
    { 
     HorizontalEndPosition = horozontalEndPosition; 
     VerticalEndPosition = verticalEndPosition; 
    } 
    public Rectangle(int x, int y, int lineThickness, int horozontalEndPosition, int verticalEndPosition) 
     : base(x, y) 
    { 
     LineThickness = lineThickness; 
     HorizontalEndPosition = horozontalEndPosition; 
     VerticalEndPosition = verticalEndPosition; 
    } 
    #endregion 

    #region Properties 
    [XmlIgnore] 
    public int LineThickness { get; set; } 
    [XmlIgnore] 
    public int HorizontalEndPosition {get; set;} 
    [XmlIgnore] 
    public int VerticalEndPosition { get; set; } 

    public override string CommandString 
    { 
     get 
     { 
      return String.Format("X{0},{1},{2},{3},{4}", X, Y, LineThickness, HorizontalEndPosition, VerticalEndPosition); 
     } 
     set 
     { 
      GenerateCommandFromText(value); 
     } 
    } 
    #endregion 

    #region Helpers 
    private void GenerateCommandFromText(string command) 
    { 
     if (!command.StartsWith(GetFactoryKey())) 
      throw new ArgumentException("Command must begin with " + GetFactoryKey()); 
     string[] commands = command.Substring(1).Split(','); 
     this.X = int.Parse(commands[0]); 
     this.Y = int.Parse(commands[1]); 
     this.LineThickness = int.Parse(commands[2]); 
     this.HorizontalEndPosition = int.Parse(commands[3]); 
     this.VerticalEndPosition = int.Parse(commands[4]); 

    } 
    #endregion 

    #region Members 
    public override void Paint(Graphics g, Image buffer) 
    { 
     using (Pen p = new Pen(Color.Black, LineThickness)) 
     { 
      g.DrawRectangle(p, new System.Drawing.Rectangle(X, Y, HorizontalEndPosition - X, VerticalEndPosition - Y)); 
     } 

    } 

    public string GetFactoryKey() 
    { 
     return "X"; 
    } 
    #endregion 
} 

DrawableItemBase:

public abstract class DrawableItemBase : Epl2CommandBase, IDrawableCommand 
{ 
    protected DrawableItemBase() 
    { 
     Location = new Point(); 
    } 
    protected DrawableItemBase(Point location) 
    { 
     Location = location; 
    } 
    protected DrawableItemBase(int x, int y) 
    { 
     Location = new Point(); 
     X = x; 
     Y = y; 
    } 
    private Point _Location; 
    [XmlIgnore] 
    public virtual Point Location 
    { 
     get { return _Location; } 
     set { _Location = value; } 
    } 

    [XmlIgnore] 
    public int X 
    { 
     get { return _Location.X; } 
     set { _Location.X = value; } 
    } 
    [XmlIgnore] 
    public int Y 
    { 
     get { return _Location.Y; } 
     set { _Location.Y = value; } 
    } 

    abstract public void Paint(Graphics g, Image buffer); 
} 

Epl2CommandBase:

public abstract partial class Epl2CommandBase : IEpl2Command 
{ 
    protected Epl2CommandBase() { } 

    public virtual byte[] GenerateByteCommand() 
    { 
     return Encoding.ASCII.GetBytes(CommandString + '\n'); 
    } 
    public abstract string CommandString { get; set; } 
} 

giao diện khác nhau:

public interface IEpl2GeneralFactoryProduct 
{ 
    string GetFactoryKey(); 
} 
public interface IEpl2Command 
{ 
    string CommandString { get; set; } 
} 

public interface IDrawableCommand : IEpl2Command 
{ 
    void Paint(System.Drawing.Graphics g, System.Drawing.Image buffer); 
} 
Các vấn đề liên quan