How should I design a game character class with a large number of attributes? - class

I am a student who is currently learning Java and trying to write a small text-based RPG game with it. The first problem I encountered in the design of the game is the 'character' class, which represents all the playable heroes and enemy characters, and is now implemented by myself as the following:
class RPGActor {
private String name;
private int HP; // hit points
private int MP; // mana
private int AP; // attack
private int DP; // defense
... // followed by tens of other attributes.
public Actor(int actorID)
{
... // Reads all attributes from a file based on the 'actorID'.
}
public void printStatus()
{
System.out.println(name);
System.out.println("HP :" + HP);
System.out.println("MP :" + MP);
... // And print all the attributes one by one.
}
public void setHP(int newHP)
{
HP = newHP;
}
public int getHP()
{
return HP;
}
public void setMP(...)
{
...
}
// And tens of accessors and mutators for each attribute
}
The problem I see with this design is that there are too many things that needs to be hand-coded: There are some 20-30 attributes in the class, and a separate accessor/mutator needs to be implemented for each of them. And the function for displaying the current status of the hero, printStatus, must output each attribute separately even though every line of output follows the exact same format. This makes the class definition tediously long.
Plus, if later I want to add one more attribute to the game, then I must remember to modify 'printStatus', and add a pair of accessor/mutator for it.
So my question is: is there a way to design the class so that I can use ONE pair of set/get functions to set all attributes. Something like:
public void set(String attribName, int attribVal)
{
...
}
and print the attributes iteratively like this:
public void printStatus()
{
System.out.println(name);
for (...)
System.out.println(curAttribName + ": " + curAttribVal);
}
Thank you very much!

I'll show you a solution, to set all attributes with one method, but you shouldn't use it, I'll tell you the reasons afterwards.
class RPGActor {
private static final String NAME = "Name";
private static final String HP = "HP";
private static final String MP = "MP";
private static final String AP = "AP";
private static final String DP = "DP";
// ... followed by tens of other attributes.
private Map<String, Object> attributes = new HashMap<String, Object>();
public RPGActor(int actorID) {
this.attributes.put(NAME, nameFromFile);
// ... Reads all attributes from a file based on the 'actorID'.
}
public void setAttribute(String attributeName, Object value) {
this.attributes.put(attributeName, value);
}
public int getAttribute(String attributeName) {
return this.attributes.get(attributeName);
}
}
This has several disadvantages:
no code-completion for setting specific attributes
less readable
...
BETTER:
Although you are using classes and objects, this isn't very object-oriented. Especially you're violating the encapsulation paradigm.
You shouldn't set the HP explicitly from outside the class itself. Only in rare use cases that is needed. Instead you should think about what the actor really does: attacking, defending, casting spells.
Therefore it should look more like this:
class RPGActor {
private String name;
private int HP; // hit points
private int MP; // mana
private int AP; // attack
private int DP; // defense
// ... followed by tens of other attributes.
private Map<String, Object> attributes = new HashMap<String, Object>();
public RPGActor(int actorID) {
// ... Reads all attributes from a file based on the 'actorID'.
}
public void attacks(RPGActor defender) {
defender.defend(this.getAttack());
}
public void defend(Attack attack) {
switch (attack.getType()) {
case PHYSICAL:
// This actor is resistant against physical attacks.
return;
case MAGICAL:
this.HP = this.HP - attack.getStrength();
break;
}
}
public void castSpell(Spell spell, Set<Target> targets) {
// targets could be other actors, equipment or chickens dependent on the spell
// ...
}
}
With inheritance or more advanced design patterns like the Strategy pattern you can make each actor react differently on attacks.

Related

this singleton has way can be improved?

i was using google's singleton but this must need too many reference.
example, when I have to use another class in my Player class that used singleton, I must be using reference three time. Like this : Player.instance.another.blank=0;
my singleton
public static Player instance;
public void Awake()
{
if(instance ==null){
instance=this;
}
else
{
if(instance!=this){
Destroy(this.gameObject);
}
}
Is there any reason to destroy the instance? Even so, we are not updating the existing instance immediately after destroying it whenever a player is added.
I have a singleton Gist that I usually use: https://gist.github.com/xepherys/34d3d5ce3f44749e8649a25b38127347
It has decent comments for anyone unfamiliar with singletons, and is threadsafe. You can remove everything except the lazy field and the constructor region. I use this as the basis for Manager classes.
using System;
// Update namespace as needed
namespace WhatsYourName
{
/*
This is the name of your threadsafe Singleton - change "SingletonLazyThreadsafe" to value that makes sense, and be sure to use your
editors [Rename] option, or update all values to match.
Just because the Singleton itself is threadsafe does not mean that all methods that might be contained are automatically threadsafe.
If threading is important, use threadsafe variables, such as:
System.Collections.Concurrent.ConcurrentDictionary<TKey,TValue>
https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2
rather than:
System.Collections.Generic.Dictionary<TKey,TValue>
https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2
Alternatively, lock() can be used in a pinch, but there is the potential for slight performance hits.
Any field, property, or method not marked with "// REQUIRED" means that it's just a sample and can be removed or changed as needed.
Comments are inline as a reminder and as a point of education for those not familiar with Singletons.
Initial snippet added 12/08/2018 - JSW (Xepherys).
*/
public class SingletonLazyThreadsafe
{
#region Fields
// Private
private static readonly Lazy<SingletonLazyThreadsafe> lazy = new Lazy<SingletonLazyThreadsafe>(() => new SingletonLazyThreadsafe()); // REQUIRED
private int changeCount;
private int myInteger;
private string myString;
// Public
public char MyPublicChar; // Note: Even though it's a field, if it's publicly accessible, I generally capitalize the first letter. This is a personal design choice. Most folk tend to use lowercase for fields regardless of their accessibility level.
#endregion
#region Properties
// Note: Private getter/setter for private field.
private int ChangeCount
{
get
{
return this.changeCount;
}
set
{
this.changeCount = value;
}
}
// Note: Public getter/setter for private field.
public int MyInteger
{
get
{
return this.myInteger;
}
set
{
this.myInteger = value;
}
}
// Note: Public getter / protected setter for private field. This allows a {get} from anywhere, but only a {set} from inside the class or derived classes.
public string MyString
{
get
{
return this.myString;
}
protected set
{
this.myString = value;
}
}
#endregion
#region Constructors
private SingletonLazyThreadsafe() // REQUIRED
{ }
public static SingletonLazyThreadsafe Instance // REQUIRED
{
get
{
return lazy.Value;
}
}
#endregion
#region Methods
// Note: This is a public method that just changes the myInteger field. It's useless since the property is public, but it's just an example. It also call IncreaseCount().
public void IncrementInteger(int value)
{
this.MyInteger = value;
IncreaseCount();
}
// Note: This is a public method that just changes the myString field. It's useless since the property is public, but it's just an example. It also call IncreaseCount().
public void ChangeString(string value)
{
this.MyString = value;
IncreaseCount();
}
// Note: This is a private method, which means it can only be called by other methods in this class, and not publicly or outside of the class. While it could directly change
// 'changeCount', I also have it making changes via the private 'ChangeCount' property, which is also only accessible inside the class.
private void IncreaseCount()
{
this.ChangeCount++;
}
#endregion
}
}

CS0120 occurs (functions) Unity

public int[,] position = new int[8, 8];
Figure pawn1 = new Figure();
void Start()
{
pawn1.Create("pawn", 1, new Vector2Int(1, 2));
}
void Update()
{
}
[System.Serializable]
public class Figure
{
public int id;
public void Create(string nameEntered, int IdEntered, Vector2Int positionEntered)
{
position[positionEntered.x, positionEntered.y] = IdEntered;
//CS0120 occurs here
}
}
Getting this error and dont know how to fix it
Is there anyone who´s able to help?
Any kinda help is appreciated
CS0120 means
An object reference is required for the nonstatic field, method, or property 'member'
The reference to
public int[,] position = new int[8, 8];
is non-static or instanced since it doesn't use the keyword static. That means the only way to access it is over the reference of the instance of the outer class.
The solution depends on what you want to do with it:
If you want it non-static so that it is not "shared" between all instances of the outer class a solution could be to pass on your outer classe's instance reference like
private void Start()
{
pawn1.Create("pawn", 1, new Vector2Int(1, 2), this);
}
And in Figure expect according value (replace <OuterType> by the outer classes actual name)
[System.Serializable]
public class Figure
{
public int id;
public void Create(string nameEntered, int IdEntered, Vector2Int positionEntered, <OuterType> reference)
{
reference.position[positionEntered.x, positionEntered.y] = IdEntered;
}
}
Otherwise you can make it static so it is "shared" between all instances of the outer class:
public static int[,] position;
Hint1
If that is all your Create method is supposed to do why not setting the value in the outer class itself?
private void Start()
{
position[1,2] = 1;
// Rather pass on the value in this direction if Figure needs it
pawn1 = new Figure("pawn", position[1,2], /*...*/);
}
Than there is no need to pass position etc on to the Figure instance and than get the value written back (unless there is happening more you didn't show).
Hint2
Instead of create a new Figure in
Figure pawn1 = new Figure();
and than later use its method Create to setup a value you should probably rather use the constructor e.g.:
[System.Serializable]
public class Figure
{
public int id;
public Figure(string nameEntered, int IdEntered, Vector2Int positionEntered, <OuterType> reference)
{
reference.position[positionEntered.x, positionEntered.y] = IdEntered;
}
}
and use it like e.g.
Figure pawn1;
private void Start()
{
pawn1 = new Figure("pawn", 1, new Vector2Int(1, 2), this);
}
Hint3
The usage of Start and Update let's conclude that you are very probably using a MonoBehaviour.
To avoid confusion with the transform.position I'ld recommend to name your field maybe better Positions.
Hint4
So far you are not using any of the Vector2Int's functionality but use it only to get the two int values.
In case you are not doing anything else with positionEntered it would be less overhead to instead of passing on a new Vector2Int only to get two int values simply pass on the int values themselves
pawn1.Create("pawn", 1, 1, 2, this);
and
public void Create(string nameEntered, int IdEntered, int x, int y, <OuterType> reference)
{
reference.position[x, y] = IdEntered;
}
Hint5
In general if you are using MonoBehaviour components but you are not using one of Start or Update etc remove them entirely from your class because Unity calls them as soon as they exist which causes unnecesary overhead.

How do I combine a Controlled Lifetime relationship type (i.e. Owned<T>) with a delegate factory?

In my application, I have a service that requires a constructor parameter not resolved by Autofac, that I instantiate using a delegate factory:
public class Service
{
public Service(string parameter /*, ... other dependencies */)
{
}
public delegate Service Factory(string parameter);
}
This works great! I really love this feature.
I also like the Controlled Lifetime relationship, so I can let my component depend on a Func<Owned<ISomething>> like this:
public class Component
{
private Func<Owned<ISomething>> _somethingFactory;
/* constructor omitted for brevity */
public void DoSomethingUseful()
{
using (var ownedSomething = _somethingFactory())
{
/* Lots of useful code here */
}
}
}
My problem is that now I want to combine the two. I can't have an instance of Func<Owned<Service>> injected, because it needs that parameter, so my current solution is to abstract the factory away into another service, say IServiceFactory:
public interface IServiceFactory
{
Service Create(string parameter);
}
...implemented as such:
public class ServiceFactory : IServiceFactory
{
private Service.Factory _internalFactory;
public ServiceFactory(Service.Factory internalFactory)
{
_internalFactory = internalFactory;
}
public Service Create(string parameter)
{
return _internalFactory(parameter);
}
}
My component then becomes this:
public class Component
{
Func<Owned<IServiceFactory>> _serviceFactoryFactory;
/* ... */
}
The need for such a field name leaves a bad taste in my mouth to the point that I suspect there must be a cleaner way to handle this case.
Is there another way?
You could change your injected factory to include the string parameter:
private Func<string, Owned<ISomething>> _somethingFactory;
Then you can pass the string to the factory when you want to create a new instance:
public void DoSomethingUseful()
{
using (var ownedSomething = _somethingFactory("my parameter"))
{
/* Lots of useful code here */
}
}
I've created a .NET Fiddle with a small working sample.

Could you explain me one OOP confusion?

I am creating a program for testing a website. Site has a registration process, which I am testing.
I have created a class named "Client", which should store the information about the client (name, family name, e-mail etc.).
Since I am testing, I use random number generator to generate a name for the client (I have the list of names, one of which is chosen randomly).
Obviously, I should create a method "generateName ()".
But the question is: in which class I should create it?
Lots of programmers would create the method in the Client class. And would do something like that:
client = new Client ();
client.generateName ();
But I have read, that this approach is incorrect - because the client does not generate name for himself. Program does.
Based on that information, I do as follows:
class Program
{
private void generateName ();
}
...
class Client
{
...
public void name ( String name )
{
this.name = name;
}
}
program = new Program ();
program.launch();
client = new Client ();
client.name ( program.generateName () );
But as I know, this approach is not used by the developers.
Could you clarify, how to know "what is right and what is wrong" here? And what information source should I use as an arbitrary for the cases of this kind?
It sounds like generateName() could be a static method in Client, since it's independent of instances:
class Client {
private String name;
public Client(String name) {
this.name = name;
}
public static String generateRandomName() {
String generatedName = ...;
return generatedName;
}
}
You could the simply pass it's value to the Client object as you currently are, or via the constructor:
client = new Client(Client.generateRandomName());
Otherwise, I'd suggest a ClientNameGenerator to handle name generation, to keep inline with SRP and to raise cohesion. This would be the better choice, seeing how you may be needing more name generation methods in the future:
class ClientNameGenerator {
public String generateRandomName() {
String generatedName = ...;
return generatedName;
}
//other name generation methods...
}
You can now use a ClientNameGenerator object to manage the generation of client names:
ClientNameGenerator nameGenerator = new ClientNameGenerator();
client = new Client(nameGenerator.generateRandomName());
Anytime you need to generate a name, simply use the ClientNameGenerator object you created.
There are a number of places which might be appropriate locations for this functionality.
You could have it as a private method on the Client class, used by a static factory method for generating Clients with a random name.
public class Client {
....
public static Client randomlyNamed() {
return new Client(randomName());
}
private static String randomName() {
return ...;
}
}
But that private method might be better extracted to a more appropriate class for generating random Strings...
public class Client {
private static final int defaultNameLength = 8;
....
public static Client randomlyNamed() {
return new Client(Strings.randomString(defaultNameLength));
}
}
public class Strings
private static String randomString(int length) {
return ...;
}
}
You could then expand the static method into a general purpose ClientBuilder class, with an instance method named something like 'withRandomName()'.
public class Client {
...
}
public class ClientBuilder {
private static final int defaultNameLength = 8;
...
public ClientBuilder randomlyNamed() {
this.name = Strings.randomString(defaultNameLength);
}
public Client build() {
return new Client(name);
}
}
public class Strings
private static String randomString(int length) {
return ...;
}
}
An alternative would be an implementation of a NamingStrategy (e.g. ``) object which is given to a ClientBuilder object.
public class RandomNames implements NamingStrategy {
private static final int defaultNameLength = 8;
public String name() {
return String.randomString(defaultNameLength);
}
}
public class ClientBuilder {
private final NamingStrategy nameSource;
public ClientBuilder(NamingStrategy nameSource) {
this.nameSource = nameSource;
}
public Client build() {
return new Client(nameSource.name());
}
}
The pure way to go would be to have a separate class ClientGenerator that produces clients. Because generating clients is not typical client behavior and in your application model a client is no more than a passive data container for client properties. However, generating clients is an activity in "the client domain". Therefor it would be defendable to create a static method Client.NewClient(), like the .NET feamework does with Guid. On the other hand, a guid's very nature is to be generated, it does not represent something in the real world. It IS a spawnable id. So the comparison may not be all that ligitimate.
A common similar mistake (or impurity if you wish) is a Save method on an object. Instead one should have a Persister class that does the job. Or a Manager. Because Save is something you can do to or with the class, not behavior of the class ifself.

Interaction between two classes

I want to know the best way to "share" or have access to attributes between 2 classes like this :
class A {
public A() {
B myClassB = new B();
}
int attributeA;
}
Class B {
int foo() {
// I want to have something like : return attributeA;
}
}
I hope that it's clear. If someone has a better way to ask the question let me know 'cause I really don't know how.
Thanks.
I would say by encapsulation you could share the fields (attributes) between classes. It is a fundamental OOP concept which helps a programmer to modify the already implemented code without breaking the code of others who use the fields.
Consider the below example (Java)
public class Person{
public String name = "John";
public int age = 25;
}
Sharing Person class's attributes with another class
public class EncapTest {
public static void main(String[] args) {
Person p = new Person();
p.name = "Tom";
p.age = 20;
System.out.println(p.name);
System.out.println(p.age);
}
}
The above approach is bad way to share(access) attributes between classes because any one can change the fields because they are public. So when you change them, they get changed for everyone (even for those who didn't want). It is like you're breaking the code of others.
See the below example which uses encapsulation
public class Person{
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getName() {
return age;
}
}
Sharing Person class's attributes with another class
public class EncapTest {
public static void main(String[] args) {
Person p = new Person();
p.setName("Tom");
p.setAge(20)
System.out.println(p.getName());
System.out.println(p.getAge());
}
}
By making the attributes private and accessing them using the getters and setters methods, you are hiding the attributes with in the class. So whenever you set the attributes in another class, you're not breaking other programmers' code.