4

In OOP I was taught that your objects should only expose their members via getters and should never allow direct mutation.

Looking at some Angular2 apps I always see this concept violated.

export class TodoService {
  todos: Array<TodoData> = [];

  addTodo(todo: string) {
    this.todos.push({
      text: todo
    });
  }
}

In the code above there's no guarantee that a todo won't be added in ways other than the addTodo method (e.g. calling todoService.todos.push({text: ''}) or even by overwriting the whole todos array).

I always try to eliminate these violation possibilities, which makes my services end up looking similar to this:

export class MyService {
  private _myPrimitive: boolean;
  get myPrimitive(): { return this._myPrimitive; }

  private _myArray: Array<number>;
  get myArray(): Array<number> { return this._myArray.splice(0); }

  private _myObject: MyObject;
  getMyObject(): MyObject { return JSON.parse(JSON.stringify(this._myObject)); }

  private _onMyEvent = new Subject<Event>();
  get onMyEvent: Observable<Event> { return this._onMyEvent.asObservable(); }
}

I don't see this style of coding in any Angular2 applications. It's not just ugly, it's also slow when it comes to change detection (firing JSON.parse(JSON.stringify(object) after every 'mousemove' event is very costly) so obviously I'm doing something wrong.

My question is:

  1. Are there more elegant ways to achieve read-only behaivour from outside MyService.

  2. Why is there no such focus on hiding unnecessary logic in Angular2 than in other OOP languages such as Java/C#/PHP.

laike9m
  • 18,344
  • 20
  • 107
  • 140
Blaž Zupančič
  • 2,176
  • 2
  • 13
  • 22
  • Why are you cloning `_myObject`? If they are built this way then they should be immutable, shouldn't they? – Günter Zöchbauer Aug 28 '16 at 16:36
  • @GünterZöchbauer That is true, you could build an immutable `myObject` but sometimes you need to store data that has public fields. Also note that immutability isn't the same as read-only (someone could still overwrite `myObject`). By cloning it I'm making a simple defensive copy (http://www.javapractices.com/topic/TopicAction.do?Id=15) because I don't want it to be modified from outside. This way the object won't be modified via public fields/methods. – Blaž Zupančič Aug 28 '16 at 17:09
  • You could use builders where classes only allow to be modified using this builder. Only when you have a reference to this builder you can update properties. If you have a lot of such classes it's convenient to use code generation for such classes with builders. – Günter Zöchbauer Aug 28 '16 at 17:14

1 Answers1

1

The reason why we have getter/setter for properties is OOP Open/Close principle. For example in Java I have a class Student like below:

class Student { 
  public: 
    int age;
}

If I expose the age as a public member like that. Later if there is a request to add a constraint that age should be under 100. I have to search all my program to check for age assignments.

In Typescript you can write like that:

class Student {
  age: number
}

Later you can modify only Student class by adding getter or setter.

class Student {
  get age() {
  }
  set age(value: number) {
  }
}

And when you use student.age = 15 it will call set age. No need to change outside code. So by default, you don't need to write getter/setter.

For questions to your answers:

  1. You can use public get and private set.

  2. That's opinion is not a fact. You can always set a member under private to hide it from outside.

Daniel Tran
  • 6,083
  • 12
  • 25
  • Sorry, but I still need some clarification of your first answer: As you can see in my question I'm already using public getters with private fields in my code, but that only makes them read-only when they are primitives. If I return an object with a getter I'm returning a reference, so the object can still be mutated via public fields/methods so it is not read-only hence the cloning in my answer. – Blaž Zupančič Aug 28 '16 at 17:23
  • You can refer to this answer when you want to clone an object: http://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-clone-an-object-in-javascript In C#, Java, C++ you have to do the same (cloning). – Daniel Tran Aug 29 '16 at 00:00
  • Sorry, but did you even read my question? I'm already using the most effecient cloning methods and also everything else you suggested in your answer. See the 2nd code clip please. – Blaž Zupančič Aug 29 '16 at 09:05
  • Yes I did read. 1. I explained the reason why you don't need getter setter by default. 2. Answer to your question: you are using the most effencient way. 3. Explain that you need to keep other OOP concepts in Angular. – Daniel Tran Aug 29 '16 at 10:02