0

In this example, I have two cases of a service that exposes an observable, test$. One through a getter, the other as a public field.

Is there any practical difference?

I have seen numerous examples of declarative programming using the field way. Is using a getter not considered declarative?

component:

@Component()
export const TestComp {
    private test$ = this.testService.test$; 
    constructor(private testService: TestService){}
}

Case 1: Service with field value:

@Injectable()
export const TestService {
    public test$ = of('test');
}

Case 2: Service with property/getter:

@Injectable()
export const TestService {

    private _test$ = of('test');
    public get test$() {
        return this._test$;
    }
}
VLAZ
  • 26,331
  • 9
  • 49
  • 67
toregh
  • 59
  • 1
  • 6
  • 1
    does the last one compile correctly (i can see name duplication) – Antoniossss Jan 24 '23 at 11:28
  • No, think of it as two cases. Choose one. I can edit to clarify. Thank you – toregh Jan 24 '23 at 11:29
  • 1
    I meant that `test$` is duplicated in the second variant as `this.test$` can point to either of those field or getter. Its not the name of TestService an issue here. – Antoniossss Jan 24 '23 at 11:30
  • 1
    now its better. – Antoniossss Jan 24 '23 at 11:31
  • 1
    "*I have seen numerous examples of declarative programming using the field way.*" I'm confused by this. Declarative programming will be trying to avoid the implementation details and focuses on *declaring intent* over details. Why should the details matter in declarative approach? – VLAZ Jan 24 '23 at 11:31
  • Good question, @VLAZ! I've just never seen anyone use a getter so I assumed it was not the declarative way to do it? – toregh Jan 24 '23 at 11:32
  • 1
    Fields were probably used because of the issue you have presented here - you have to have _test and get test (boilerplate code, who wants that?). For the consumer, it will be the same as as having readonly public field. – Antoniossss Jan 24 '23 at 11:33
  • @Antoniossss, yes, maybe. It is easier to test with property/getter in my experience. If there is no difference, I'd prefer to use getters test wise – toregh Jan 24 '23 at 11:36
  • 1
    but in tests (consumer) it will not matter, how is that different to you? – Antoniossss Jan 24 '23 at 11:36
  • 1
    Could be just down to not needing a getter in this case. Again, *declarative* shouldn't really rely on specific implementation. That's *why* it exists, to abstract away those details. Focusing on the details of why a certain declarative approach chose to do something seems backwards. – VLAZ Jan 24 '23 at 11:37
  • @Antoniossss The case when I mock the service. Let's say this.testService.test$ actually returns a httpcall-observable, and I'd like to mock it. With a getter I could, in the test, use something like spyOnProperty(testService, 'test$', 'get').and.returnValue('of('mockedData')). I can't do that (to my knowledge) with fieldvalue. I have to use createSpyObj('testService, {method: null}, {test$: of('mockedData'}) – toregh Jan 24 '23 at 11:40
  • 1
    In my tetss, it would not matter as my mock would simply be `{test$:of()}`, as mocked service is not under tests here therfore implementation does not matter even more. Rarely I use spies. – Antoniossss Jan 24 '23 at 11:46
  • @Antoniossss Hmm, I am trying to understand what you mean. Do you have an example of this? The way you mock it, I mean? Maybe a blog post if that is easier? – toregh Jan 24 '23 at 11:50
  • @Antoniossss maybe like this? https://stackoverflow.com/a/50947723/4667462 – toregh Jan 24 '23 at 11:53
  • 1
    Yes, something like this. For methods I use jasmine.createSpyobj(["method"])) to create a partial stub with "mehod" method defined. If I need fields, I just provide plain objects. If units tests indeed tests UNIT of code and not half of the application, then this is usually enough (and fast and readable and maintainable) – Antoniossss Jan 24 '23 at 11:54
  • @Antoniossss Using that approach, let's say for each unit test, the property has to be different. For one it should be {test$:of()}, and for the other it should be {test$:of('someThingElse')}. Wouldn't it be necessary to update the provider using the testBed for each test? With spyOnProperty, the mockService would be defined once and changed with spyOnProperty for each test. – toregh Jan 24 '23 at 12:44
  • @Antoniossss Thank you btw for taking the time to help me with this :) – toregh Jan 24 '23 at 12:45
  • the point is, you dont need implementation of your service to be present in your test only to mock a chunk of its implementation to thest something else and there fore be dependent how it was actually implemented (like you pointed out, it will be different for eg fields an setters) – Antoniossss Jan 24 '23 at 13:11
  • @toregh code template for angular component test already define testbed configuration if `beforeEach` callback. Moreover, you can use TestBed.overrideProvider. Also it is possible to define shared Subject and use that as observable (then the provider is constant) but that is purely scenario dependent – Antoniossss Jan 24 '23 at 13:13

2 Answers2

1

The following are the advantages I can think of for using getters/setters

  1. Adding appropriate access specifiers and validation logic before retrieving the property of the object.

  2. Avoiding Anemic domain model. This is to make sure classes that describes domain has its own logic and validations instead of services handling all of the business logic.

A bit more reading on Anemic domain model https://en.wikipedia.org/wiki/Anemic_domain_model

Tej
  • 414
  • 2
  • 7
1

I would say it is a matter of convinience and less boilerplate code.

For example

interface Service{
  readonly name:String;
}

class Service1 implements Service{
  constructor(public readonly name:string){
  }
}

class Service2 implements Service{
  private _name:string;
  get name():string{
    return this.name;
  }
// + some wat to set/mutate name internally - ctor/method
}

Bot implementations correctly implements Service declaration and it does not matter to the consumer which implementation is used. So in you case, it just a matter of preferences of the author

Antoniossss
  • 31,590
  • 6
  • 57
  • 99