2010-04-19 22 views
6

Tôi có hai phương pháp này trên một lớp chỉ khác nhau trong một cuộc gọi phương thức. Rõ ràng, điều này là rất không-DRY, đặc biệt là cả hai đều sử dụng cùng một công thức.Làm cách nào để mã C++ này có nhiều DRY hơn?

int PlayerCharacter::getAttack() { 
    int attack; 
    attack = 1 + this->level; 
    for(int i = 0; i < this->current_equipment; i++) { 
     attack += this->equipment[i].getAttack(); 
    } 
    attack *= sqrt(this->level); 
    return attack; 
} 
int PlayerCharacter::getDefense() { 
    int defense; 
    defense = 1 + this->level; 
    for(int i = 0; i < this->current_equipment; i++) { 
     defense += this->equipment[i].getDefense(); 
    } 
    defense *= sqrt(this->level); 
    return defense; 
} 

Làm cách nào tôi có thể dọn dẹp trong C++?

+1

'this.'? Đăng cho chúng tôi một số mã thực. :) – GManNickG

+0

GMan nói gì. Ngoài ra, là các biến toàn cầu 'tấn công' và' phòng thủ 'hay bạn đã bỏ qua định nghĩa của chúng? – sbi

+0

* facepalm * - Vâng, đây là C++ thực đầu tiên của tôi sau nhiều tháng của các ngôn ngữ khác: P. Có thể tệ hơn, tôi có thể thực hiện 'self.' vì phần lớn nó là Python. – Macha

Trả lời

11

Theo tôi, những gì bạn có là tốt, vì nó sẽ cho phép bạn tinh chỉnh tấn công/phòng thủ nhiều hơn nếu bạn đại diện cho cả hai người trong số họ với một chức năng. Khi bạn bắt đầu thử nghiệm trò chơi của mình, bạn sẽ bắt đầu cân bằng các công thức tấn công/phòng thủ, do đó, có các chức năng riêng biệt cho chúng là tốt.

Toàn bộ khái niệm về DRY [đừng lặp lại chính mình] là [hy vọng] để ngăn mã của bạn trở thành một bản sao lớn & dán liên hoan. Trong trường hợp của bạn, các công thức phòng thủ/tấn công sẽ thay đổi theo thời gian [ví dụ, nếu nhân vật có buff/trạng thái-ailment thì sao? Một trạng thái cụ thể có thể làm giảm khả năng phòng thủ một nửa, trong khi tăng tấn công bằng 2 (Berserk, FF reference, heh)]

+1

Tôi đồng ý. Trong khi có (theo lý thuyết) cách để DRY-ify toán học cơ bản của những gì bạn đang làm (sử dụng một số con trỏ hàm), bạn có khả năng sẽ viết lên các phương thức bao bọc khác nhau cho từng đường dài, như (như @ ItzWarty gợi ý) bạn bắt đầu giả mạo với hai giá trị theo những cách khác nhau. –

16

Một cách dễ dàng là đại diện cho tất cả các thuộc tính của thiết bị trong một mảng.

enum Attributes { 
    Attack, 
    Defense, 
    AttributeMAX 
}; 

class Equipment { 
    std::vector<int> attributes; 

    Equipment(int attack, int defense): attributes(AttributeMAX) 
    { 
    attributes[ATTACK] = attack; 
    attributes[DEFENSE] = defense; 
    } 

}; 

Sau đó, bạn thay đổi chức năng của bạn để

int PlayerCharacter::getAttribute(int& value, Attribute attribute) { 
    value = 1 + this->level; 
    for(int i = 0; i <= current_equipment; i++) { 
     value += this->equipment[i].attributes[attribute]; 
    } 
    value *= sqrt(this->level); 
    return value; 
} 

Và bạn có thể gọi nó như vậy

player.getAttribute(player.attack, Attack); 
+0

Nghĩ về điều này quá, nhưng đã quá lười biếng để đánh vần nó ra. =) – Nailer

+2

Đây là một giải pháp tốt vì nó không làm xáo trộn ý định của mã. Tuy nhiên, nó có xu hướng làm xáo trộn việc sử dụng. Nhưng đó là DỄ DÀNG cố định bằng cách tạo ra hai thói quen đóng gói được gọi là getAttack và getDefence mà lần lượt các cuộc gọi với thói quen. (EDIT) Nhưng tôi sẽ nói rằng player.getAttribute (player.attack, Attack) thực sự rõ ràng ở chính quyền của nó. – Torlack

+1

+1. Bạn sẽ cần phải khởi tạo 'thuộc tính' cho số thuộc tính trước khi gán cho nó trong ctor, tất nhiên. – Bill

4

tốt, tôi ít nhất sẽ xem xét giải nén sqrt(this.level); như một chức năng riêng biệt gọi là getLevelModifier()

defense = 1 + this.level; 

attack = 1 + this.level; 

có thể

defense = getBaseDefense(); 

attack= getBaseAttack(); 

Điều này không chỉ thêm tính linh hoạt, nó cũng tự động văn bản chức năng của bạn.

5

Từ một điểm refactoring nghiêm ngặt của xem, bạn có thể làm điều này:

int PlayerCharacter::getDefense() { 
    return getAttribute(&EquipmentClass::getDefense); 
} 

int PlayerCharacter::getOffense() { 
    return getAttribute(&EquipmentClass::getOffense); 
} 

int PlayerCharacter::getAttribute(int (EquipmentClass::*attributeFun)()) { 
    int attribute = 0; 
    attribute= 1 + this->level; 
    for(int i = 0; i <= current_equipment; i++) { 
     attribute += this->equipment[i].*attributeFun(); 
    } 
    attribute *= sqrt(this->level); 
    return attribute; 
} 
1

IMO, ItzWarty làm cho một điểm hợp lý - bạn có thể muốn chỉ để lại mã một mình. Giả sử bạn quyết định rằng việc thay đổi đó là một điều tốt tuy nhiên, bạn có thể làm một cái gì đó như thế này:

class equipment { 
public: 
    int getAttack(); 
    int getDefense(); 
}; 

int PlayerCharacter::getBattleFactor(int (equipment::*get)()) { 
    int factor = level + 1; 
    for (int i=0; i<current_equipment; ++i) 
     factor += equipment[i].*get(); 
    return factor * sqrt(level + 1); 
} 

Bạn muốn gọi đây như:

int attack = my_player.getBattleFactor(&equipment::getAttack); 

hay:

int defense = my_player.GetBattleFactor(&equipment::getDefense); 

Sửa :

Một khả năng hiển nhiên khác là để quyết định rằng bất kỳ một phần thiết bị nào có thể chỉ là phòng thủ hoặc gây khó chịu.Trong trường hợp này, mọi thứ trở nên đơn giản hơn vẫn còn, đến mức nó thậm chí có thể là vấn đề cho dù bạn thực sự cần một chức năng ở tất cả:

class PlayerCharacter { 
    std::vector<equipment> d_equip; 
    std::vector<equipment> o_equip; 

// ... 

int d=level+1+std::accumulate(d_equip.begin(), d_equip.end(), 0)*sqrt(level+1); 

int o=level+1+std::accumulate(o_equip.begin(), o_equip.end(), 0)*sqrt(level+1); 
+0

+1 cho 'BattleFactor'. Nghe có vẻ tuyệt vời và làm cho chúng tôi muốn chơi nó (bất kể nó là gì). –

1

Ngoài câu trả lời ltzWarty 's Tôi muốn giới thiệu refactoring vòng lặp của bạn thành một chức năng cho khả năng đọc tốt hơn:

int PlayerCharacter::getEquipmentAttack() { 
    int attack = 0; 
    for(int i = 0; i <= current_equipment; i++) { 
     attack += this.equipment[i].getAttack(); 
    } 
    return attack; 
} 
int PlayerCharacter::getAttack() { 
    int attack = 1 + this->level; 
    attack += getEquipmentAttack(); 
    attack *= sqrt(this->level); 
    return attack; 
} 

Ngoài ra, khi bạn khai báo biến cục bộ attack bạn nên initialize it immediately.

2

Tùy thuộc vào mã khác trong ứng dụng nó có thể hoặc có thể không có giá trị nhưng cách tiếp cận OOP sẽ làm cho các đối tượng giá trị phòng thủ và tấn công của một lớp thay vì đồng bằng int. Sau đó, bạn có thể lấy được chúng từ một lớp cơ sở chung có phương thức get() gọi phương thức getEquipmentRate() ảo được định nghĩa bởi mỗi lớp con khi cần thiết.

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