2013-01-10 17 views
5

Tôi cần sự giúp đỡ của bạn. Tôi đang ở giữa việc sắp xếp một kịch bản có thể kiểm tra các điều kiện khác nhau trước khi một khả năng có thể được thực hiện trong một trò chơi RPG.C#, Unity - Chức năng đơn lấy nhiều đối tượng khác nhau

Tất cả những khả năng này nằm trong các lớp riêng lẻ (Fireball, Heal, Poison) đều xuất phát từ một lớp trừu tượng khác (Khả năng, khả năng chữa bệnh, khả năng DOT), tất cả đều được cấp cho lớp trừu tượng (Ability).

Để tránh tạo ra nhiều chức năng, để xử lý tất cả các khả năng duy nhất:

void condition(Fireball f){//test}; 
void condition(Heal f){//test}; 
void condition(Poison f){//test}; 

tôi đang cố gắng để tạo ra một cuộc gọi chức năng duy nhất có thể thực hiện tất cả các loại khả năng.

void condition(Ability f){//test} 

Cho đến nay tôi đã thành công trong việc tạo đối tượng Fireball và chuyển nó cho hàm.

Fireball _fire = new FireBall(); 
condition(_fire); 

void condition(Ability f){//test} 

Từ đây tôi có thể truy cập tất cả các biến công khai được khởi tạo trong lớp Khả năng, nhưng tôi không thể truy cập biến công khai được khởi tạo trong lớp có nguồn gốc.

Có phải tôi là người đang quên thứ gì đó hay tôi đang xem xét điều này ở góc độ sai? (Tôi không giỏi sử dụng các lớp thừa kế và trừu tượng.)

+0

Tính đa hình OOP cổ điển/vấn đề kế thừa :) Thật khó và thú vị khi nghĩ đến một giải pháp sẽ bảo toàn đóng gói và sử dụng thoải mái. +1. – dreamzor

+0

Điều này liên quan gì đến Mục tiêu C? –

+0

Rất tiếc, tôi đoán tôi đã gắn thẻ sai. –

Trả lời

8

Nếu không biết thêm chi tiết về chức năng của điều kiện, bạn có hai lựa chọn.

One, bạn có thể làm một cái gì đó giống như

if (f.GetType() == typeof(FireBall)) 
{ 
    fireBall = (FireBall)f; 
    fireBall.FireTheFireBall(); 
} 
else if (f.GetType() == typeof(Heal)) 
... 

Hoặc, Khả năng của bạn có thể có một phương pháp trừu tượng Kích hoạt, mà tất cả các lớp thừa kế được yêu cầu phải quá tải:

class Fireball 
{ 
    public override void Activate() 
    { 
     //do fireball specific things 
     this.FireTheFireBall(); 
    } 

    public void FireTheFireBall() {...}  
} 

class Heal 
{ 
    public override void Activate() 
    { 
     //do healing specific things 
     this.ApplyTheBandage(); 
    } 
    ... 
} 

abstract class Ability 
{ 
    public abstract void Activate(); 
} 

void condition(Ability f){ 
    f.Activate(); //runs the version of Activate of the derived class 
} 

Sau đó, bất cứ điều gì mà làm việc với khả năng có thể gọi someAbility.Activate() và việc triển khai thực hiện bởi lớp dẫn xuất sẽ được thực hiện.

Bạn cũng nên nghiên cứu về các giao diện, giống như các lớp trừu tượng. Lợi ích của giao diện là bạn có thể thực hiện nhiều trong số chúng, trong khi bạn bị giới hạn thừa kế từ chỉ một lớp trừu tượng cơ sở. Hãy suy nghĩ về một giao diện IKnob có chức năng Turn and Pull. Bạn có thể có một lớp ngăn kéo triển khai IKnob, một lớp Door, một lớp TrappedDoor, thực hiện Turn và kích hoạt một cái bẫy. Thủ đi đến một cánh cửa, và chạm vào nút Sử dụng vào nó, và bạn vượt qua với chức năng mở các đối tượng, Open (IKnob núm)

void Open(IKnob knob) 
{ 
    knob.Turn(); 
    knob.Pull(); 
} 

class TrappedDoor:IKnob,IMaterial,ISomethingElse,IHaveTheseOtherCapabilitiesAsWell 
{ 
    private bool TrapAlreadySprung{get;set;} 
    //more complex properties would allow traps to be attached either to the knob, or the door, such that in one case turning the knob activates the trap, and in the other, Pull activates the trap 
    public Turn() { 
    if(! TrapAlreadySprung) 
    { 
     MessageBox("You hit your head, now you're dead"); 
    } 
    } 
} 

Có cách để kiểm tra xem một cái gì đó có một giao diện, vì vậy nếu một số người chơi đi tới một mục và cố gắng nói chuyện với nó, bạn có thể kiểm tra xem đối tượng có giao diện ICanTalk không, nếu nó có gọi object.GetReply ("Hello") và đối tượng có thể trả lời. Vì vậy, bạn có thể có cửa nói chuyện và đá nếu bạn muốn. Bạn nhận được tất cả các mã của bạn xử lý nói chuyện với mọi thứ/hiển thị phản hồi, vv làm việc với các phương thức giao diện ICanTalk, và sau đó các lớp khác có thể thực hiện ICanTalk và mỗi người quyết định cách họ phản hồi. Khái niệm này được gọi là "tách mối quan tâm" và giúp bạn tạo thêm mã tái sử dụng.

Điều quan trọng là bạn có thể viết một đoạn mã, thuật toán, chức năng, v.v. chỉ hoạt động với giao diện đó, và theo cách đó khi bạn nhận được mã đó làm việc với giao diện, bạn có thể sử dụng giao diện đó bất kỳ lớp nào và lớp đó có thể tận dụng mã có sẵn.

I.e. hàm condition của bạn, nếu nó có trong giao diện IAbility, khi bạn có mã đó hoạt động, thì bất kỳ lớp nào bạn tạo để triển khai IAbility đều có thể được chuyển tới hàm điều kiện. Hàm điều kiện chịu trách nhiệm thực hiện bất cứ điều gì mà nó đòi hỏi, và lớp thực hiện IAbility sẽ quan tâm đến bất cứ điều gì cụ thể cho nó bên trong các phương thức mà nó đã thực hiện.

Tất nhiên, các lớp triển khai lớp trừu tượng hoặc giao diện phải triển khai các phương thức được yêu cầu, vì vậy đôi khi bạn có thể cảm thấy mình đang sao chép mã. Ví dụ, nếu bạn có các lớp tương tự, như TrappedDoor và Door, một TrappedDoor có thể hoạt động giống như một cánh cửa bình thường nếu cái bẫy không được thiết lập/đã bung ra. Vì vậy, bạn có thể kế thừa từ Cửa trong trường hợp này, hoặc có một tài sản Cửa riêng (được gọi là "thành phần"). Nếu bẫy đã được bung, sau đó bạn có thể gọi vào các cửa cơ sở lớp hoặc tài sản cửa riêng và gọi .Turn để bạn chỉ cần sử dụng lại hành vi mặc định của một cánh cửa thường xuyên trong trường hợp bẫy không hoạt động.

Test if object implements interface

Cá nhân tôi chủ yếu sử dụng các giao diện và thành phần, thay vì thừa kế. Không phải là thừa kế nó khủng khiếp, nhưng hệ thống phân cấp thừa kế có thể nhanh chóng trở nên rất phức tạp.

+0

Cảm ơn bạn đã trả lời. Tôi không chắc chắn cách đề xuất thứ hai hoạt động, nhưng tôi thực sự nghĩ rằng đề xuất đầu tiên có thể hoạt động. Tôi sẽ xem xét nó càng sớm càng tốt và tôi sẽ báo cáo lại :) –

+0

@Marc Đã cập nhật để mở rộng ví dụ thứ 2. – AaronLS

+1

Ví dụ thứ hai trông giống như một ý tưởng hợp lý hơn tôi nghi ngờ ... :) –

Các vấn đề liên quan