I am learning SOLID principles. I read a lot of things about the single responsibility principle but I don't get it exactly. I will give an example that what do I want to say.
Let's say we have an article service for managing articles. How should I design article service by single responsibility.
Is like that:
class ArticleService{
create(){};
read(){};
update(){};
delete(){};
}
Or create a class for each operations like that :
class ArticleCreateService{
create(){};
}
class ArticleReadService{
read(){};
}
// and so on ...
According to single responsibility which is the best way to managing articles?
Thanks.
As Robert C. Martin (Uncle Bob) says about Single Responsibility Principle:
There should never be more than one reason for a class to change
So your first option is better to comply with Single Responsibility principle:
class ArticleService {
create(){};
read(){};
update(){};
delete(){};
}
As when you will want to edit ArticleService, then there is just one reason to edit this class. And this reason is to edit Article.
The second version looks like Interface Segregation principle. However, it should be slightly modified.
class ArticleCreateService
{
void Create() { }
}
class ArticleReadService
{
void Read() { }
}
At first, we need to segregate methods of class. We can segregate by Create() and Read() methods.
So let's create these interfaces:
public interface IReadable
{
void Read();
}
public interface ICreatable
{
void Create();
}
And modified version of ArticleService could look like this:
public class ArticleService : IReadable, ICreatable
// public class ArticleService implements IReadable, ICreatable // in TypeScript
{
public void Read()
{
throw new NotImplementedException();
}
void void Create()
{
throw new NotImplementedException();
}
}
The S in solid stands for that a class or method should only have one reason to change. That means that a class, module or method should have a single well defined responsibility.
In this particular case you might want to (for whatever reason) extend read, or write etc to read and write from/to different sources for example. Therefore keeping those responsibilities in a class each, will make it easier to extend i.e:
read class -> only reads data -> this class can then be extended with more methods like readFromExcel or readFromDB. Reading is a single responsibility. In that class each method can have separate niches of that one responsibility i.e readFromExcel only has one responsibility i.e readingFromExcel only.
class Read {
readFromExcel();
readFromDB();
}
A good rule of thumb is: does my class have one single responsibility? and what is that responsibility? Can my method and classes be extended without them losing that one single responsibility? In the above example class read has (S)ingle responsibility of only reads data and within it method readFromDB(); only reads files from the database.
Related
Hi im really questioning the usage of a "asObserveable()" call on subject.
In my view it creates a big unecassary overhead.
The prevention of calls like "next()" or "complete()" are in my view useless.
Can you name me a good reason why you should do this?
Just compare this two
Without
export class TestService {
public get test$(): Observable<test> {
return this.test$.asObservable();
}
public get test2$(): Observable<test> {
return this.test2$.asObservable();
}
public get test3$(): Observable<test3> {
return this.test3$.asObservable();
}
public get test4$(): Observable<test4> {
return this.test4$.asObservable();
}
private readonly _test1$ = new ReplaySubject<test1>(1);
private readonly _test2$ = new ReplaySubject<test2>(1);
private readonly _test3$ = new ReplaySubject<test3>(1);
private readonly _test4$ = new ReplaySubject<test4>(1);
}
Without
export class TestService {
public readonly test1$ = new ReplaySubject<test1>(1);
public readonly test2$ = new ReplaySubject<test2>(1);
public readonly test3$ = new ReplaySubject<test3>(1);
public readonly test4$ = new ReplaySubject<test4>(1);
}
Downsides of subject's asObservable
Some boiler-plate code. Not much else.
In my view it creates a big unnecessary overhead.
Have you measured it? Subjects extend Observables, all it does is create a shallow copy. I'd be surprised if you found an app where the difference is bigger than the variance (effectively not measurable).
Upsides of subject's asObservable
A cleaner architecture means it's easier to find bugs and/or easier to stop bugs from being created in the first place.
Encapsulation is one of the fundamentals of a clean architecture. Because encapsulation hides some part of our program from other parts, it makes each part a bit easier to reason about. Therefore it is easier to understand, write, extend, and maintain.
If there is a bug in a well architected system, the surface area is much smaller.
In general, the benefits for these sorts of decisions tend to make themselves known on larger projects with bigger teams. If you're writing a hobby project at home or making a minimal viable product by yourself or a small team, it might makes sense to forgo over-planning in order to make haste. Such a project might need a re-write/overhaul once it grows, but by then the added effort will be worth it.
Alternatives to Subject's asObservable
If you have a static type checker set relatively strictly (TypeScript, Elm, PureScript, ClojureScript, etc), then you can just return the Subject as an Observable without making any changes to the runtime representation of the type.
You get the encapsulation with zero run-time cost.
I can give a good reason. Suppose, you want to emit some event execCompleted$ based on certain action . Here is how it is implememted
export class TestService {
private readonly execCompleted$ = new Subject<string>();
public async makeServerCall(userData){
const resp = await this.http.post('...').toPromise();
this.execCompleted$.next(resp.data.id);
return resp;
}
public getUpdatedId(){
this.execCompleted$.asObservable();
}
}
Now, this service is used by two Components -> Admin and UserInfo.
The Admin component can update some user info by using makeServerCall() and update some user data. This event has to be captured by UserInfoComponent so that it can listen and update the info (once some update has been performed).
By exposing Subject using getUpdatedId() , you are restricting any other component to mistakingly send the next() event on execCompleted$. This Subject will only get triggered when makeServerCall(userData) is called.
This is just a simple example. There are more complex cases but all has the same underlying intention.
To not allow unwanted events to be emitted or cancelled. Only the intended source should so that.
This is a standard practice in programming where you want some restrictions on what can be extended by others and what can't. Kinda like open-closed principal.
Let's say I have two classes, where you can observe over some observables.
First example, with public subject:
class EventsPub {
public readonly onEnd = new Subject<void>();
}
Second example, with private subject and registering method:
class EventsPriv {
private readonly endEvent = new Subject<void>();
public onEnd(cb: () => void): Subscription {
return this.endEvent.subscribe(cb);
}
}
The first example is somehow unsafe because anyone can call eventsPub.endEvent.next() from outside the class and introduce side effects, however, comparing to example 2 It allows for pipes, which is a big plus since developers can for ex. register only for the first event with eventsPub.onEnd.pipe(first()).subscribe(cb).
The second example also allows for one-time subscription but requires more code and ugly unsubscribing.
const subscription = eventsPriv.onEnd(() => {
// logic..
subscription.unsubscribe()
});
From your point of view, which is the best way to go? Or maybe there is a better solution?
This is based a lot on my personal preference but I'd do it like this:
class EventsPriv {
private readonly endEvent = new Subject<void>();
get endEvent$(): Observable<void> {
return this.endEvent;
}
}
So inside the class I'll use endEvent while I can still use it eg. in a template with obj.endEvent$ | async and from the outside it behaves like an Observable.
Note, that in fact I'm returning the same instance of Subject. The only thing that restricts the outside world from misusing it with obj.endEvent$.next() are Typescript's type guards. If I was using just JavaScript or if I typecasted it to any I could call next.
This is actually the recommended way of exposing Subjects instead of using the asObservable() operator. You can notice that this is used everywhere internally in RxJS 5. For example if you look at repeatWhen synopsys:
public repeatWhen(notifier: function(notifications: Observable): Observable): Observable
You can see that the notifier function receives an Observable as a parameter (you can see it in the code here as well https://github.com/ReactiveX/rxjs/blob/5.5.6/src/operators/repeatWhen.ts#L29).
But if you look into the code where the function is called you'll see they are in fact passing a Subject and not an Observable: https://github.com/ReactiveX/rxjs/blob/5.5.6/src/operators/repeatWhen.ts#L114-L115.
This has been discussed on RxJS GitHub page and reasons for this are performance and that the Typescript type guards are sufficient. You can read more in these discussions:
https://github.com/ReactiveX/rxjs/pull/2408
https://github.com/ReactiveX/rxjs/issues/2391
Let say I have a model class
class User {
name: string;
email: string;
}
and a service related to user
class UserService {
getInfo (usr: User) {
console.log(usr);
}
}
The usr: User is an input parameter for getInfo method, and no User class is created for that statement since the User just "typing" the usr. Instead, we can replace it with interface, isn't it?
Yeah, I am aware of Angular 2 official guide that suggest try to use class instead of interface
quote: "Consider using a class instead of an interface"
and I can accept it, although it is weird.
My question is, WHY WHY WHY use a class to "type" a variable, since we don't create any instance from it? We can replace the UserService class as shown below, and it works too...
class UserService {
getInfo (usr: any) { //this work too
console.log(usr);
}
}
(please correct me if I am wrong)
[#1] This will create a property that have the class content
class UserService {
usr: User = User; //usr = { name: '', email: ''}
}
[#2] This is used by Angular 2 to do dependency injection (assuming I have listed the User as provider), and in this case, it is similar to #1 as we are just copy the User to usr. (usually, we do DI on service, instead of model, since injecting model will have the same effect as "assigning" it to a variable, am I correct?)
class UserService {
constructor (private usr: User) {}
}
[#3] And this confuse me... because why we still do it since we don't need it? (because nothing is created, no usage here)
class UserService {
usr: User; //usr: any; (?)
}
class UserMoreService {
getInfo (usr: User) {console.log(usr);} //usr: any (?)
}
Lastly, may I know what is the motivation behind to create a "model" class in Angular 2 project?
We have the component to display data, and to get the data, we can ask it from related service, and the service will get it from the server.
Can you see this? we don't need a "third party" model to "hold" the data, because everything can be done without declaring a "model" class. Can please tell me when to create a "model"? I don't see any reason to create that...
It is not a must but just a suggestion.
Why?
An interface-class can be a provider lookup token in Angular dependency injection.
Source: https://angular.io/styleguide#!#03-03
Creating a class is pretty much the same work with respect to creating an interface but you can use these classes as a class-interface in your provider tokens later on.
{ provide: MinimalLogger, useExisting: LoggerService },
This can be used as a type of inheritance among Angular providers.
export abstract class MinimalLogger {
logs: string[];
logInfo: (msg: string) => void;
}
When you use a class this way, it's called a class-interface. The key benefit of a class-interface is that you can get the strong-typing of an interface and you can use it as a provider token in the way you would a normal class.
Source: https://angular.io/docs/ts/latest/cookbook/dependency-injection.html#!#class-interface
Side note: If you're sure that you aren't going to use it as a provider token or so IMO you should be using an interface since they disappear after the code is transpiled to JavaScript. Hence, they don't use your memory.
Using a class as an interface gives you the characteristics of an interface in a real JavaScript object.
Of course a real object occupies memory. To minimize memory cost, the class should have no implementation
I found one thing in javascript of my WPF project. It has called
window.external.ShowWindow();
I have found that method is written in class InteropClass like below.
[ComVisible(true)]
public class InteropClass
{
public void ShowWindow()
{
// logic
}
}
and that method is called.
I am trying to analyse it, for that I have used already built class like below
[ComVisible(true)]
public partial class AnotherClass : SomeDifferentClass
{
public void AnotherMethod()
{
// logic
}
}
and tried to call it as
window.external.AnotherMethod();
but it is not working. Error alert says AnotherMethod is not supported by window.external object
I know both classes differs in many terms and I can make it work but my question is What rules to be followed to make this work, may be like class must be directly inherited from Object or some other.
What you need to do is set the ObjectForScripting property on the web
browser control to an object containing the C# methods you want to
call from JavaScript.
from here told me everything I was missing.
Suppose we have the following code:
[Test.js file]:
class Test {
...
public static aStaticFunction():void {
...
this.aMemberFunction(); // <- Issue #1.
}
private aMemberFunction():void {
...
this.aStaticFunction(); // <- Issue #2.
}
}
[Another.js file]:
class Another {
...
private anotherMemberFunction():void {
Test.aStaticFunction(); // <- Issue #3.
}
}
See the Issue #x. comments for the issues (3) I want to address.
I've been playing with some configurations by now and I don't get it all yet.
Can you help me to understand how can I access this methods in the three places?
Thanks.
There is some code below, but there are some important concepts to bear in mind.
A static method does not exist on any instance. There are good reasons for this:
It can be called before you have created a new instance
It can be called from outside an instance, so you wouldn't know which instance the call was related to
So in all cases where you call the static method, you need to use the full name:
Test.aStaticFunction();
If the static method needs to call an instance method, you need to pass that in. This does set off alarm bells for me though. If the static method depends on an instance method, it probably shouldn't be a static method.
To see what I mean, think about this problem.
If I call Test.aStaticFunction() from outside of an instance, when 100 instances have been created, which instance should the static function use? There is no way of telling. If your method needs to know data from the instance or call methods on the instance, it almost certainly shouldn't be static.
So although the code below works, it probably isn't really what you require - what you probably need is to remove the static keyword and make sure you have an instance to call in your other classes.
interface IHasMemberFunction {
aMemberFunction(): void;
}
class Test {
public static aStaticFunction(aClass: IHasMemberFunction):void {
aClass.aMemberFunction();
}
private aMemberFunction():void {
Test.aStaticFunction(this);
}
}
class Another {
private anotherMemberFunction():void {
Test.aStaticFunction(new Test());
}
}
this is related to an instance whereas static members are independent of any instance. So if you want to access members of an instance within a static member you have to pass it in. However in that case I don't see a reason for having a static member in the first place. I believe you need two functions. one static and one non-static. That do two different things, so :
class Test {
public notaStaticFunction():void {
this.aMemberFunction(); // <- Issue #1.
}
public static aStaticFunction():void {
}
private aMemberFunction():void {
this.notaStaticFunction(); // <- Issue #2.
}
}
class Another {
private anotherMemberFunction():void {
Test.aStaticFunction(); // <- Issue #3.
}
}
That said you can share properties between static and member functions using static properties.
dont use class name like Class.staticMethod(), use this:
this.constructor.staticMethod()
to maintain the inheritance of static methods
Edit: as mentioned in comments, typescript does not support this.constructor. There is an open ticket in it's issue tracker, but not much progress over last 5 years.