My question is about how to properly use Factory constructors when dealing with relatively complext immutable objects. Suppose I want to return an instance of a class with some of the properties changed.
Example
#immutable
class SomeObject {
final int id;
final String name;
final int upvote;
final int downvote;
final int favorite;
SomeObject({this.id, this.name, this.upvote, this.downvote, this.favorite});
factory SomeObject.upvoted() {
return SomeObject(
upvote: this.upvote + 1 // apparently can't use this keyword here, wrong syntax
);
}
SomeObject.upvoted(SomeObject ref) {
id = ref.id;
// cant change an immutable type
}
SomeObject upvoted() {
return SomeObject(
upvote: upvote + 1,
//... other properties are null, bad idea?
);
}
SomeObject upvotedWithDefaultConstructorUsingReference(SomeObject ref) {
// works but seems like an overkill, especially considering more number of properties
return SomeObject(
id: ref.id,
name: ref.name,
upvote: upvote + 1,
downvote: ref.downvote,
favorite: ref.downvote
);
}
}
The SomeObject.upvoted() would be an instance of the same class but its upvoted property is +1 more than the referenced one. And there will be more like as downvoted(), withNameChanged() or copyWith().
The first 2 are constructors and others are just methods that return an instance of SomeObject class. What should be the approach here? How can I use factory constructors while the class is immutable? Also I'm not sure about the difference of these 4 examples.
I have already read the answers for this question but it doesn't seem to answer mine.
Looks like you want a copyWith type pattern:
class SomeObject {
final int id;
final String name;
final int upVote;
final int downVote;
final int favorite;
SomeObject({this.id, this.name, this.upVote, this.downVote, this.favorite});
SomeObject copyWith({
int id,
String name,
int upVote,
int downVote,
int favorite,
}) {
return SomeObject(
id: id ?? this.id,
name: name ?? this.name,
upVote: upVote ?? this.upVote,
downVote: downVote ?? this.downVote,
favorite: favorite ?? this.favorite,
);
}
}
You could adapt this in whichever way you like: upVoted copies with upVote incremented, leaving the rest the same, or allowing them to be changed.
SomeObject upVoted() {
return SomeObject(
id: id, // no need for 'this' here
name: name,
upVote: upVote + 1,
downVote: downVote,
favorite: favorite,
);
}
Combining the two you could come up with endless variations:
SomeObject upVoted() => copyWith(upVote: upVote + 1);
SomeObject downVoted() => copyWith(downVote: downVote + 1);
SomeObject upVoteRetracted() => copyWith(upVote: upVote - 1);
... which then makes you start to wonder why this class is immutable. It seems like it would make more sense to have it hold and mutate its state, rather than making multiple copies with different values.
Related
I have something similar to the following code snippets. I am simplifying the code here for attempted brevity.
First, a subclass of QAbstractListModel with the following data() implementation, and Q_INVOKABLE get_thing() method, which returns a pointer to a QObject subclass, QML_thing:
QVariant data(QModelIndex& index, int role) {
const auto& thing = m_data.at(index.row()); // shared pointer to QML_thing
switch(role)
{
case Qt::DisplayRole:
return thing->name(); // this works
case WholeThingRole:
return QVariant::fromValue(QML_thing*>(thing.get());
}
}
QML_thing* getThing(int index) const
{
const thing = m_data.at(index); // shared pointer
return thing.get();
}
Next, I have a Repeater in a QML file that has this code:
Repeater {
id: repeater
model: thingModel
ThingDelegate {
thing: wholeThing // This calls the role, but ends up being null
}
onItemAdded {
item.thing = model.getThing(index) // this works, but 'breaks' the binding
}
}
My question is: why doesn't the thing: binding in QML work, but the thing = version does?
I am having troubles incrementing the value of my instance variables. I tried making a method so that for every pet I buy, it will add that much to how many I already have. But when I print dogs variable, it says 0 even though I added 2. I'd appreciate any help. Thanks!
public class myStuff
static int dogs;
static int cats;
public static void main(String[] args) {
myStuff.buy(dogs, 2);
System.out.println(dogs);
}
public static void buy(int pet, int howMany) {
pet = pet + howMany;
}
}
you cant do that in java, since it is pass-by-value
In Java, method parameters are passed by value (which means the value of dogsin your case is passed in the first Place, but never touched). Objects however, are manipulated by reference. So, if you want to increase the number of pets, you could use a class Pet with a value count
public class Pet {
private int count;
public Pet(int count) {
this.count = count;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
If you then pass an instance of Pet to your buy function and increase the count via setCount, the value will be saved.
I'd like to know which of the following is a better OOP practice when having private methods. Should the fields be used directly, or given to the method like an argument?
class MyClass {
private int myNumber;
public MyClass(int number) {
this.setMyNumber(number);
}
public void setMyNumber(int number) {
this.myNumber = number;
}
public int getMyNumber() {
return this.myNumber;
}
public int getTransformedNumber() {
return transformNumberInSomeWay1();
// OR
return transformNumberInSomeWay2(this.getMyNumber());
}
private int transformNumberInSomeWay1() {
int number = this.getMyNumber();
<... transformation of the number ...>
return number;
}
private int transformNumberInSomeWay2(int number) {
<... transformation of the number ...>
return number;
}
}
If I'm using the transformation number method for other things than the field myNumber, the second way is better. What about the case when I'm not actually using it for anything else, yet I don't know how will the development of the class go? Should I always do it the second way or let the people who need it, change it later on?
I have a question to OOP in general. I'd like to demonstrate a snippet of code:
public class Test
{
private int someInt = 0;
public Test()
{
this.method1(this);
this.method2(this.getSomeInt());
}
public void method1(Test test)
{
System.out.print(test.getSomeInt());
}
public void method2(int someInt)
{
System.out.print(someInt);
}
public int getSomeInt()
{
return this.someInt;
}
}
My question here: Which of those two function calls is more efficient? Does it matter if I use the getter as a parameter or should I give the object as the parameter from the beginning? Could you explain which one I should prefer because I don't really know what to stick to. The object seems to be too big as a parameter, the parameters look too cascaded. I apologize in advance if any related question has been asked already, I didn't know what to look for. ,_,
I really can't find how to decide which option is the best. I have a class "Product" which has a "RecommendedAge" for the animal intended to be fed, among other attributes.
Please note that I just want the different values from RecommendedAge for each species, I don't care about their numerical equivalent (what would be represented with a Discriminated Union, but I can't use that due to ORM issues).
As "RecommendedAge" possible values differ depending on the type of Animal de Product targets, I don't know which of the following (or any other) approach is the best (all pseudocode):
Class Product {
Id id;
String name;
RecommendedAge recommendedAge;
}
AbstractClass RecommendedAge {
}
Class DogRecommendedAge : RecommendedAge {
readonly const Int Big;
readonly const Int Small;
}
Class CatRecommendedAge : RecommendedAge {
readonly const Int Medium;
readonly const Int Tiny;
}
Or something like:
AbstractClass Product {
Id id;
String name;
}
Enum DogRecommendedAge {
Big = 0;
Small = 1;
}
Enum CatRecommendedAge {
Medium = 0;
Tiny= 1;
}
Class DogProduct : Product {
DogRecommendedAge dogRecommendedAge;
}
Class CatProduct : Product {
CatRecommendedAge catRecommendedAge;
}
This may be a very silly one, but I'm stuck. Thank you all.
What about just storing recommended ages during runtime instead of at compile time? In your pseudo:
class RecommendedAges {
readonly const RecommendedAge dog = new RecommendedAge(2, 5); // example value
readonly const RecommendedAge cat = new RecommendedAge(3, 4); // example value
}
Class Product {
Id id;
String name;
RecommendedAge recommendedAge;
}
Product p = new Product(1, 'dog food', RecommendedAges.dog);