Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Oop exercise #353

Merged
merged 8 commits into from
Sep 29, 2022
Merged

Oop exercise #353

merged 8 commits into from
Sep 29, 2022

Conversation

Tedzury
Copy link
Contributor

@Tedzury Tedzury commented Aug 29, 2022

OOP exersice

Demo |
Code base

The code is submitted in a dedicated feature branch.

Only code files are submitted.

Please, review.

@github-actions
Copy link

Hey!

Congratulations on your PR! 😎😎😎

Let's do some self-checks to fix most common issues and to make some improvements to the code before reviewers put their hands on the code.

Go through the requirements/most common mistakes listed/linked below and fix the code as appropriate.

If you have any questions to requirements/common mistakes feel free asking them here or in Students' chat.

When you genuinely believe you are done put a comment stating that you have completed self-checks and fixed code accordingly.

Also, be aware, that if you would silently ignore this recommendation, a mentor can think that you are still working on fixes. And your PR will not be reviewed. 😒

A Tiny JS World -- OOP exercise check list

Relates to A Tiny JS World OOP exercise.

Check-list - definition of done

Minimal requirements to meet:

  • Implement a base class to inherit from
  • Employ default parameters
  • Each species is represented with its own class
  • No need to specify species at instantiation
  • Classes for species that do not have hands by natural design, do not consequently have hands or any equivalent property and do not inherit such properties from any base classes
  • All inhabitants are stored in a container (array or object)
  • JS native features are intensively employed (const, let, Array.map|join|forEach|..., etc)
  • Object methods like keys, values, entries as well as for...in shouldn't be used as these do not guarantee any particular order of keys/values
  • Properties used to form an object presentation string must be explicitly listed/specified; Use Array.map and Array.join to build a presentation string from the list.
  • A parent class shouldn't know anything about its children's props. Child classes must have good reasons to assign or refer to any parent's props. There are no such reasons in this task. Method overloading is to the rescue when building an inhabitant's presentation string.
  • OOP, SOLID and DRY principles are intensively employed

Optional level up (not required to implement):

  • Friends list is a list of objects refs rather than names (strings)
  • Cat-woman class is built employing composition rather than inheritance only

Bonus:

  • toString magic method; when implemented print(inhabitant) does the job as .toString is called implicitly
  • this.constructor.name; when used for output then no need to store species property

Helpful resources:

Universal recommendations:

  • Give variables and functions meaningful names. Avoid generic names like item, element, key, object, array or their variations. Exception: helper functions that are specifically and intentionally designed to be multipurpose.
  • Function names should start with a verb as they denote actions; variables are normally nouns; boolean variables/functions start with is, does, has etc; variable containing multiple entities and functions returning lists contain entity name in plural form.
  • Have consistent code style and formatting. Employ Prettier to do all dirty work for you.
  • Use common sense or seek for an advice whenever requirements look ambiguous or unclear.

Also take a note of the requirements above and follow them in all your future projects.

By the way, you may proceed to the next task before this one is reviewed and merged.

Sincerely yours,
Submissions Kottachecker 😺

@Tedzury
Copy link
Contributor Author

Tedzury commented Aug 30, 2022

Good morning. For the first view in self-check, everything seems to be alright. Humbly asking for review :)

@stale
Copy link

stale bot commented Sep 13, 2022

This issue has been automatically marked as stale because there were no activity during last 14 days. It will be closed in 7 days if no further activity occurs. Thank you for your contributions.

А. Чому так?
Найбільш розповсюджена причина: Студент не реагує на коментарі змінами коду і не задає запитань через брак часу або зміну життєвих пріоритетів. Покинуті піари відволікають менторів. Коли у студента з'явиться час, він/вона зможе перевідкрити той самий піар і продовжити роботу.

Б. Що робити, якщо в піарі нема оновлень, оскільки не зрозуміло, що треба зробити?
Варіант 1. Задати питання в самому PR.
Варіант 2. Задати питання в студентському чаті.

В. А якщо я все зробив(ла) і це ментор не рев'юває мої зміни?

  1. Переконайся, що ти відреагував(ла) на всі коментарі або кодом, або запитаннями, або відповідями. Напиши в PR і в чаті, що чесно вважаєш, що все зроблено і попроси повторне рев'ю. Якщо щось не зрозуміло, задай запитання.
  2. Реагуй на коментарі як менторів, так і інших учасників, включаючи ботів.
  3. Не ігноруй прохання типу * "Let's do some self-checks ..." * "Go through the checklist below..." * "mark fulfilled requirements..." * "if you would silently ignore this recommendation, a mentor may think that you are still working on fixes"
    навіть якщо вони написані ботом. Боти помічники і ментори покладаються на те, що прохання і пропозиції бота дотримуються.
    Не лінись піти по лінках в коментарях, погуглити термінологію та скористатись Google Translate.
  4. Можливо, у менторів склалися інші пріоритети через роботу, сімейні обставини і т.п. В такому разі, якщо ти зробив(ла) рекомендоване вище, то волай в чаті, що PR позначений stale, наче, все зроблено, а ментори чомусь не реагують - рятуйте!

Г. Хіба недостатньо того, що я додав(ла) коміт із змінами?
Часто буває так, що бачиш новий коміт, ідеш перевіряти, змін багато, доводиться перечитувати весь код. А потім з'ясовується, що одна невеличка зміна "відкладена на потім" чи з'являється ще один коміт і знов треба перечитувати все. Любіть нас, спілкуйтеся з нами - і ми відповімо повною взаємністю.

Традиційна пропозиція: задай питання по вищенаписаному в студентському чаті.

@stale stale bot added the 💤 Stale label Sep 13, 2022
Copy link
Member

@OleksiyRudenko OleksiyRudenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Tedzury good job!
Yet implementation needs to follow OOP and SOLID principles.

Comment on lines 8 to 13
"species",
"name",
"gender",
"saying",
"friends",
"legs",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A base class should never handle child classes' properties. This breaks Encapsulation, Open-Closed and Separation of Concerns principles as any changes to child classes would require changes in one (or more) base class up the hierarchy.

Look into method overloading to have consistent interface and user experience where method implementation depends on class yet allows to re-use parent class' method for a similar purpose.

constructor(name, gender, saying, friends) {
super(name, gender, saying, friends);
this.species = "human";
this.legs = 2;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

legs looks like a shared property. Should be generalized to a base class just like any other shared property.
Same refers to species.

The criteria of generalization is property per se. It can bear different values for different class instances.

@stale stale bot removed the 💤 Stale label Sep 14, 2022
@Tedzury
Copy link
Contributor Author

Tedzury commented Sep 14, 2022

Good afternoon. Removed legs from base properties, added class Mammal and put legs to it.

Not sure that I get you right with this: "Look into method overloading to have consistent interface and user experience where method implementation depends on class yet allows to re-use parent class' method for a similar purpose."

I tried to implement that in such way, to prettify the printing output in needed order, added ternary operator into output func to modify the order of properties. Hope, it's what was needed.

@OleksiyRudenko OleksiyRudenko self-assigned this Sep 20, 2022
Copy link
Member

@OleksiyRudenko OleksiyRudenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Tedzury quite a decent job.
Let's make it more OOP/SOLID compliant.
Method overloading and extended super API are to the rescue.

"species",
"gender",
"legs",
"hands",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every class should handle the props it owns immediately.
As a developer you do not want to touch base classes when adding a new one. This is what encapsualtion and open-closed principles from OOP/SOLID are about.


const tinyWorldInhabitants = [barbos, sonya, oleksii, victoria, anjela];

tinyWorldInhabitants.forEach((item) => print(item.output()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

item is a generic term.

"saying",
"friends",
])
: (this.prettyOutput = this.propsArray);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nested ternaries considered a bad practice. Can we make this fragment more obvious? KISS

@Tedzury
Copy link
Contributor Author

Tedzury commented Sep 22, 2022

Good night. Slightly refactored output function. Removed propsArray, applied function overrading to output( ) method and modified it into child classes.

Copy link
Member

@OleksiyRudenko OleksiyRudenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Tedzury well done.
Let's polish it using learnings from past tasks.


class Mammal extends Creature {
constructor(species, name, gender, saying, friends, legs) {
super(species, name, gender, saying, friends);git
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

git here should have thrown an error. Didn't it?

}

output() {
return `${this.name}; ${this.species}; ${this.gender}; ${this.saying}; ${this.friendsToString()}`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More than 2 items. Array#join is still a thing.

Won't be a really good approach, but think of how destructuring could help you in more complicated cases:
const { propA, propC, propB } = this

@Tedzury
Copy link
Contributor Author

Tedzury commented Sep 24, 2022

Good day. Removed typo, apllied destructuring to properties in output( ) method.

Copy link
Member

@OleksiyRudenko OleksiyRudenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Tedzury let's optimize hierarchy.
Introducing classes just for the purpose of setting default values is an overkill.
I am not even sure we need classes for men and women. Or shy dogs and cats do not deserve sheDog/Cat and heDog/Cat classes?

output() {
const { name, species, gender, saying } = this;

return `${name}; ${species}; ${gender}; ${saying}; ${this.friendsToString()}`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's employ Array#join. Change this line only.
Remember, anything can be an array element. [ func() ] will add a result from func call as an array element.

Comment on lines 21 to 29
class Mammal extends Creature {
constructor(species, name, gender, saying, friends, legs) {
super(species, name, gender, saying, friends);
this.legs = legs;
}
output() {
return `${super.output()}; ${this.legs}`;
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mammal is the only class that extends Creature. Why do we need Mammal?
KISS

Comment on lines 31 to 35
class Animal extends Mammal {
constructor(species, name, gender, saying, friends) {
super(species, name, gender, saying, friends, 4);
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Animal extends Mammal just to pass a specific value.
Solvable by setting default argument value.
KISS

@Tedzury
Copy link
Contributor Author

Tedzury commented Sep 26, 2022

Good night. Removed unneded animal class, man and woman. Just passing legs as default value, and gender to humans as an argument. Also implemented array.join in output method

Copy link
Member

@OleksiyRudenko OleksiyRudenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Tedzury nearly there

output() {
const { name, species, gender, saying, friends } = this;

return `${name}; ${species}; ${gender}; ${saying}; ${friends.join(", ")}`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is still repetition ("; "). Pack whatever you need into an array and Array#join.
You will benefit a lot from recognizing use cases for Array methods.

}
}

class Mammal extends Creature {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mammal is the only class that extends Creature. Why do we need Mammal?

Any good reason for this?

Also, obviously there is no good reason to prefer 4 for legs. Decisions should be explainable.

@Tedzury
Copy link
Contributor Author

Tedzury commented Sep 27, 2022

Good morning. Left only base class and Human/Dog/Cat, legs are passed as default property in child classes. Implemented array with props to use array.join in the output method.

Sorry, but I feel very confused, I had exactly the same way of array.join in output two commits ago, but I undestood you in such way, that I shouldn't use it, so tried everything else, except manipulations with array :(

Copy link
Member

@OleksiyRudenko OleksiyRudenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Tedzury great job done!
Make sure to put some learnings on your diary.

this.saying = saying;
this.friends = friends;
this.legs = legs;
this.propsArray = [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Data type name as a part of variable name is not necessary (think if you switch to Map data type, you'd need to rename variable to reflect the change).
Plural noun is just sufficient.

Also props is quite specific term and kinda technical (as in e.g. "component props"). Here we mean "properties of inhabitant", just a normal word that in real life is never abbreviated. Do not save keystrokes on names, IDE will help you to autocomplete even German-language-style variable names.

@OleksiyRudenko OleksiyRudenko merged commit 49f6fc4 into kottans:main Sep 29, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants