-
Notifications
You must be signed in to change notification settings - Fork 17
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
遷移 Course Connector 至 v3 與重構 Course Connector 實作細節 #221
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because I didn't understand the functional requirements, I only reviewed the pure code part.
Maybe some parts are copied and pasted from the original code. If so, please just ignore my comment of them.
part 'course.g.dart'; | ||
|
||
@JsonSerializable() | ||
class Course { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are several suggestions of the Course
class:
-> prevent putting unneeded instance member into class declaration, or make them as private member.
There are many public members are just for type conversion in the constructor body.
for example, the idString
. If we don't talk about other aspects, we may still assume that this variable won't be accessed by others. In other words,
final myCourse = Course(...);
final fooBar = myCourse.idString; // x
this won't happened.
So you may consider making it to a private member, or just remove it through other ways of implementation.
-> prevent using late
late
keyword enforces a variable's constraints at runtime instead of at compile time and since the variable is not definitely initialized, every time it is read, a runtime check is inserted to make sure it has been assigned a value. If it hasn’t, an exception will be thrown.
So in this case, I'd suggest declare those to be a nullable type property. Regardless of other issues, if a class's member value is definitely not possible to be assigned during construction, declare it as a nullable property. Then, implement null checks among code. This may be because the concepts of null-safety is to clearly distinguish null and non null resource. Hence in this case, if we can not knows a variable's value, it make sense to let it become a null value.
for example:
- late int id;
+ final int? id;
-> prevent creating static
only class
In Dart, we declare a method of a class to static
may be because we need a named utility class which provide some utility methods. for example:
final class MathUtil {
static double squareOf(double n) => n * n;
}
void foo() {
log(MathUtil.squareOf(9.9).toString());
}
This often happened when we want to:
- Share a bundle of functions, variables across a large scope.
- Avoid initialization of the class.
However, this may also have the following issues:
- makes the code more difficult to maintain and test
- If lots of static members, including functions and variables in code, it becomes harder to define the scope of those static things, making encapsulation failed.
- subclasses cannot override static methods (reduce versatility)
These are just some general opinions. Lets dive into the Course
class.
In this class, there're two methods defined to be a static
function, which are:
_convertPeriodSlotsToCoursePeriods
_isNullOrEmpty
As we have known, a static
member is something that OTHERs can use, without initialize an instance.
Here, those methods are declared as private, and only used in the constructor body.
That means, they are only used by the instance itself, and not for others (private).
So it seems there is no any requirement for declaring them as static
.
In addition, we can find there're tones of old code using static
to declare a whole class.
This is advised to be avoided.
The official Dart doc has also mentioned this case here: https://dart.dev/tools/linter-rules/avoid_classes_with_only_static_members
So base on these concerns, I'd suggest always create a class for defining its instance behaviors, and use solo function or DI when we need something to be shared across code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the suggestion of the "prevent putting unneeded instance member into class declaration, or make them as private member.":
- The
Course
class is used to transfer the node from web crewler into data transfer object, it expected to transfer the dirty String into good value and assign into member. - Since I expected to input the string and set the transfer value into class. I'm not sure how to deal with it, so I add
late
when it will transform into differnt type (string -> int
orstring -> List<String>
w/ new line split). - It should be mentioned that
JsonSerializable
seems that not support transfer the constructor withList<String>
parameter 🤔, therefore theteacherString
andteachers
exists.
For the suggestion of the "prevent using late":
- I assume it is late binding the data into member. So I add it.
It may need some good implement to optimize the I optimize it on dfcf849.Course
class but I just can do it with dirty code as far.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optimized on dfcf849
id = JsonInit.intInit(idString); | ||
name = JsonInit.stringInit(name).trim(); | ||
stage = JsonInit.doubleInit(stageString); | ||
credit = JsonInit.doubleInit(creditString); | ||
periodCount = JsonInit.intInit(periodCountString); | ||
category = JsonInit.stringInit(category).trim(); | ||
teacherString = JsonInit.stringInit(teacherString).trim(); | ||
teachers = teacherString.split(RegExp(r"\n")).map((element) => element.trim()).toList(); | ||
periodSlots = JsonInit.listInit<String>(periodSlots); | ||
coursePeriods = _convertPeriodSlotsToCoursePeriods(periodSlots); | ||
classroomString = JsonInit.stringInit(classroomString).trim(); | ||
classrooms = classroomString.split(RegExp(r"\n")).map((element) => element.trim()).toList(); | ||
classNameString = JsonInit.stringInit(classNameString).trim(); | ||
classNames = classNameString.split(RegExp(r"\n")).map((element) => element.trim()).toList(); | ||
applyStatus = JsonInit.stringInit(applyStatus).trim(); | ||
language = JsonInit.stringInit(language).trim(); | ||
syllabusLink = JsonInit.stringInit(syllabusLink).trim(); | ||
note = JsonInit.stringInit(note).trim(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest not doing huge data content checks in the constructor, because:
-
Maybe cause unneeded performance waste.
Although we expect some of data may include strange things, we can still do such check when we really need to use them (at other layer of architecture, separating Model and DTO). That means, if we only need variablea
, then we just checka
without checkingb
, ifa
is not based onb
. -
JsonSerializable
will actually help us do most of things like this.
In the generated.g.dart
file, there will be several general solutions of string parsing be implemented, if we have clearly declared variable data type, even it is a nullable type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above: #221 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optimized on dfcf849
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest following #221 (comment).
applyStatus: courseRowData[14].text, | ||
language: courseRowData[15].text, | ||
syllabusLink: "", | ||
note: courseRowData[16].text)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: courseRowData[16].text)); | |
note: courseRowData[16].text,),); |
Suggest adding trailing commas, and do format & analysis.
@ntut-xuan For this comment: #221 (comment) Based on our discussions, it seems that we need not only using stricter conversion for both A better approach for replacing static-member class
|
Description
Warning:這個 PR 是重構相關 PR, diff 過大非常抱歉,僅需確認功能性正常。
在這個 PR 上,我遷移了 CourseExtraJson 與其相關無用的 Json 架構,使 Course 與 Syllabus 來涵蓋大部分的無用 Json,並且遷移了 Course Connector 至 v3
Implementation
Testing Instructions