Should a complex unifying class be doing computation?

I have a large application in Java filled with independent classes which are unified in a PlayerCharacter class. The class is intended to hold a character’s data for a game called the Burning Wheel, and as a result, is unusually complex. The data in the class needs to have computations controlled by the UI, but needs to do so in particular ways for each of the objects in the class.

There are 19 objects in the class PlayerCharacter. I’ve thought about condensing it down, but I’m pretty sure this wouldn’t work anywhere in the application as it stands so far.

private String name = ""; private String concept = "";
private String race = ""; private int age = -1;  

private ArrayList<CharacterLifepath> lifepaths = new ArrayList<>();

private StatSet stats = new StatSet();
private ArrayList<Affiliation> affiliations = new ArrayList<>();
private ArrayList<Contact> contacts = new ArrayList<>();
private ArrayList<Reputation> reputations = new ArrayList<>();
//10 more lines of declarations

I’ve been considering this problem for some time now, and have considered multiple
approaches. The problem arises primarily when data is deleted – for instance, since pretty much everything else (but not some parts!) depends upon Lifepaths, when a lifepath is deleted, nearly everything else must be recalculated. However, if a Skill is deleted, only a few things must be recalculated.

Additionally, the application must somewhere track certain values; skill points, trait points, etc. to ensure that the user is not unintentionally exceeding those values.

So my question is generally as follows: Where should everything go? What makes this easiest? There are a couple options:

  • Place point total calculations in the PlayerCharacter class (but how does this generate a warning?)
  • Handle all calculation outside of the PlayerCharacter class, and just use PlayerCharacter as a container for all the character’s information
  • Place all calculations in the PlayerCharacter class; after each item is changed, recompute the entire character. Then, if there are issues arising from deletion, throw warnings back at the UI.

I’m slightly overwhelmed by the scope of this particular class – whereas everything I’ve done so far has been easily broken down into small manageable chunks, this beast seems to resist being tamed. If there’s a better approach to this, I’m all ears! But as of right now, I’m slightly confused, and progressing aimlessly probably won’t get me anywhere. Any advice is appreciated.

I apologize if this is unclear – I’m certain I lack the software vocabulary to properly communicate my ideas. I would love to improve this question – any help here is appreciated!

Separate interface from implementation. “PlayerCharacter” should be an interface with methods like “getStats”, “getContacts”, “getReputation” etc. This gives you freedom to play with the implementation without having to change the code which uses this interface.

The implementation can choose to cache values and recompute them if necessary. For example a method “getStats” would return the value of “stats” variable; and if the variable is empty, it would be recomputed first. Methods like “removeLifepath” could remove all cached lifepath-related values.

The methods for actual recomputation could be placed to yet another class, if needed. Thus you would have: A) the interface, B) one or more classes doing the calculation, and C) an implementation keeping the cached values, and calling recalculation when necessary.

Quick sketch:

interface PlayerCharacter {

    List<Lifepath> getLifepaths();

    void addLifepath(Lifepath lifepath);

    void removeLifepath(Lifepath lifepath);

    Skills getSkills();

}

class PlayerCharacterImpl {

    List<Lifepath> lifepaths = new ArrayList<>();
    Skills cachedSkills = null;

    public List<Lifepath> getLifepaths() {
        return Collections.unmodifiableList(lifepaths);
    }

    public void addLifepath(Lifepath lifepath) {
        lifepaths.add(lifepath);
        cachedSkills = null;
    }

    public void removeLifepath(Lifepath lifepath) {
        lifepaths.remove(lifepath);
        cachedSkills = null;
    }

    // thread-unsafe
    public Skills getSkills() {
        if (null == skills) {
            skills = PlayerCharacterCalc.calculateSkills(lifepaths);
        }
        return skills
    }

}

class PlayerCharacterCalc {

    static Skills calculateSkills(List<Lifepath> lifepaths) {
        Skills skills = new Skills();
        for (Lifepath lifepath : lifepaths) {
            skills.apply(lifepath);
        }
        return skills;
    }

}

Isolate & Encapsulate Complexity

Pull calculations into their own “function” classes. Their structure and relationships is very highly dependent on your existing design.

Maybe turn affiliations, contacts, etc. into classes. This makes PlayerCharacter more usable by “function” classes.

With PlayerCharacter now a composite of other classes and individual properties as appropriate you can easily create “data” classes. These data classes are intended as customizations for the functional classes. The function class code will have the look and feel of dealing with a coherent class, not a jumble of PlayerCharacter and other classes.

Scrub your Design

You will need to re-analyze to make sure you get fundamental functionality into the right classes. For example each “data” class may have its own “delete” function that does very specific and minimal things. A “function” class, likewise, will orchistrate all the “data” classes in the overall delete process and and do “delete-y” things that are of an inter-class context.

Get a High-Level View of Design Patterns

Learn some basics about the various patterns. This will give you some insight as to how you might tackle the problem. Don’t sweat the class diagrams just get the ideas and motivation behind them. I’m not saying you should explicitly implement a particular pattern or any pattern at all. You’re just looking for inspiration.

Flyweight – Shared State.

Visitor – Performing the same operation on a collection of different types

Template Method – defines an algorithm in a base class using abstract operations that subclasses override to provide concrete behavior.

Adapter
Convert the interface of a class into another interface clients expect.
Adapter lets classes work together, that could not otherwise because of incompatible interfaces.

Façade – Provide a unified interface to a set of interfaces in a subsystem. Façade defines a higher-level interface that makes the subsystem easier to use.

This will be a partial answer since I’m not a regular user of Java (more of a C++ guy myself).
Here’s a few ideas that could guide you :

1) If a attribute may be edited by multiple sources (a Stat calculated with a base value, bonus/malus from a skill and bonus from multiple lifepath), perhaps should you not save it as a single value, but as a association of value; and it could be used to limit the total value if you wand to be sure it will not leave some boundaries. For example, something like that :

public class StatModifier {
  public String source; // the 'editing' source (ex : a lifepath)
  public String target; // the edited Stat (ex : strength)
  public int modifier;
}

public class Stat {
  private int base;
  private ArrayList<StatModifier> modifiers;
  private int computedValue;
  private int actualValue;
  private bool computed;

  public function addModifier(modifier) { // removeModifier() is basically the same
    modifiers.add(modifier);
    computed = false;
  }
  public function getValue() {
    if (!computed) {
      computedValue = base;
      // TODO : here compute value (ex : sum modifiers)

      actualValue = Math.min(computedValue, 100); // Max stat value : 100
      computed = true;
    }
    return actualValue;
  }
}

If you use this method, then the modifications of a Lifepath is a list of modifiers, and when you add/remove a lifepath, you just have to add/remove the associated modifiers. Those modifiers would logically be present within the Lifepath instance, and the PlayerCharacter class will only have to get this list and process it.

2) if you need to limit the total value of a group (say, a character cannot have more than 50 skill points across all skills), you could use a SkillSet class (for exemple) who would work as a list of skills and a validator. That way, the PlayerCharacter class only have to request the validate() method of all these class to be sure that the character is valid, and the actual validation rules will be managed set by set.

3) keep a list of the modification done since the last validation. That way, if you detect that the user is not valid, you can rollback them.

4) if you use ‘1)’, a validation step could also be to check that the sources of each modifier (still) exists.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật