Repository: a project on the remote git server (e.g. Github, GitBucket).
Master: the remote summarized code (alive code) on the git server.
HEAD: the local summarized code.
Snapshot: a committed local version.
Branch: all actions are rendered into branches, dividing from the original; a developer works on a branch.
For codes, the code exists in remote repository, in local repository (e.g. under .git folder) and in local code.
If these are identical, the project is synchronized.
* is for marking a character, ** is for marking many. ** can mark whole path to a file as well e.g. **a** can mean path/a*.* files. The dot (.) means all files at actual and all its sub-folders.
In case there are pushed snapshots on the server, these will be under the name of the contributor.
Such snapshots are ideal for having backups of local codes as well.
Fetching is ideal for reviewing codes without merging the checked code with local repository. For this, change the actual branch as needed. When finished, change the actual branch to the one in which you work and use pull for synchronizing.
In order to send a pushed snapshot to the master contributor(s), use the Pull Request function on the server.
A master contributor of the reposition (owner) will review the changes, if satisfied, will merge the branch with the Master code.
Usual message for work-in-progress snapshots (not for Pull Request): WIP.
For those files which should not be included in file tracking, put those into .gitignore.
EXAMPLE FOR ADDING A FILE TO LOCAL AND REMOTE REPOSITORIES:
Best practices are:
for small function objects, prefer lambdas before anonymous classes
for method references, use lambda instead
if needed, for functional interfaces use the pre-defined ones in java.util.function.Function
when declaring a functional interface, always use the @FunctionalInterface annotation
in case a lambda would need many statements in a code block, organize it into an external (helper) method (short and self-explanatory lambda)
in lambda, avoid referencing the input variable types, use them only when the compiler asks for it
avoid braces when have 1 parameter in lambda
using return statement in statement part of lambda is optional, do not use it
EXAMPLE FOR METHOD REFERENCE:
EXAMPLE FOR LAMBDA INSTEAD OF METHOD REFERENCE:
EXAMPLE FOR ORGANIZING BLOCK-BODY LAMBDA:
EXAMPLE FOR AVOIDING VARIABLE TYPES IN LAMBDA:
EXAMPLE FOR USING BRACES WITH LAMBDA:
EXAMPLE FOR AVOIDING RETURN STATEMENT IN LAMBDA:
When designing a method, the return value represents the purpose of the method.
Narrowing down the type onto the lower level (e.g. ArrayList<Integer>) forces the other developer to accept such type when designing his or her own methods.
It is a good practice to design a return value which can be used in several ways, via several types, so the other developer can have its own options.
In case an implementation would be the down-narrowed type, always use its interface as a type.
In many occasions, the pre-defined conversion methods with which a series of data can be created, will offer interface as type from the beginning.
EXAMPLE FOR USING INTERFACE AS RETURN TYPE 1:
EXAMPLE FOR USING INTERFACE AS RETURN TYPE 2:
The installed application has an application root folder from which all sub-folders is created. The sub-folder structure depends on whether a build system is used for development, and if used, depends on the used build system (Maven, Gradle etc.).
EXAMPLE FOR GETTING APPLICATION ROOT FOLDER:
The installed application will not contain source folders (e.g. /scr folders under Maven). Different folders will be accessed by default in development and in production cycle.
The application under IntelliJ IDEA (with or without Maven) can use the following folders for keeping the .java source files:
/src
/src/main
/src/main/java
When accessing these folders, use File.separator instead of / character for platform independence.
Because these folders rarely exist under installed application but the original user dir is not accessible in the development cycle, it is a good practice to check all of them against file existence. Once a file is found, that will point out which folder should be used as application root folder.
Also, it is possible to create numerous unique folders directly under the project root. These folders will be available in the development cycle. With application initialization, always check these folders for existence and if needed, create them before usage.
EXAMPLE FOR DECLARING ALL POSSIBLE APPLICATION ROOT FOLDERS:
For test environment, Maven uses the src/test/resources folder to read and write files as default.
In order to get access to it, use getAbsolutePath() method of File, or get() method of Paths combined with getAbsolutePath().
In order to access the resources folder under the target folder (which is the target folder of compiler under Maven), use getClassLoader() method of ClassLoader combined with getAbsolutePath().
EXAMPLE FOR GETTING TEST RESOURCES FOLDER:
EXAMPLE FOR GETTING TARGET RESOURCES FOLDER:
In OOP, inheritance is used to express that the subclass is a special class of the superclass, in other words presents relationship between the super and the sub.
Code re-usage means that a code can be a composite of previously created codes in case those codes can be used somewhere else as well.
Composition means that the code must show the meaning and purpose of the code. The right composition offers better understanding, better test-ability.
Based upon these three principles, inheritance should not be used for decreasing the amount of written code but not expressing the meaning of inheritance.
Also, inheritance should not decrease the readability and unity (the composition) of the code.
The right composition expresses the strategy itself while inheritance hides it.
EXAMPLE FOR WRONG USAGE OF CODE (INHERITANCE BUT NO COMPOSITION):
In the above code the inheritance does not express that the MonthData is a special kind of AnnualData.
Also, the subclass is fragile, can harm encapsulation, any changes in the super class can break the subclass.
The correct solution following OOP principles would be the full composition of the second class, and no inheritance.
However, in case the complexity would grow over the edge, it can be good to out-sourcing the common code into a superclass (interface) like below.
EXAMPLE FOR RIGHT USAGE OF CODE (NO INHERITANCE BUT COMPOSITION):
EXAMPLE FOR RIGHT USAGE OF CODE (INHERITANCE AND COMPOSITION):
Software engineering is a systematic and procedural approach to software development. It usually consists of collecting and documenting requirements from the users, designing and outlining the platform structure, coding or application development, user and developer testing, as well as after launch operations and maintenance support.
There are some basic principles governing good software engineering:
KISS
DRY (or DIE)
SOLID
The KISS (Keep It Simple, Stupid) principle: all software or application design and deployment should be done as simple as possible, with the least complexities and clear to understand development procedures. This principle ensures that the source code is made easy to debug (whenever required) and future maintenance by any other operations and maintenance software engineer becomes easier. Usually it is advised to keep only one method for one use case, in order to comply with the KISS principle.
The DRY or DIE (Don't Repeat Yourself or Duplication Is Evil) principle: every software engineer should aim to reduce repetition of information or methods within their work in order to avoid redundancy. Therefore, it's recommended to segregate the entire system of consideration into fragments. Dividing the code into smaller segments can help manage the code and use a single segment at any point, by calling, whenever required.
The SOLID principles:
S (Single responsibility) principle: every module should be designed to carry out just its responsibility over a single part of the functionality provided by the software
O (Open-closed) principle: any part of the software should be available and easily extended for further features, but should not be modified
L (Liskov substitution) principle: objects within a program can be replaced with instances of their sub-category types without changing the accuracy of that specific program
I (Interface segregation) principle: it is best to avoid adding new functionalities or methods to an existing interface. Rather it is recommended to incorporate a new interface and permit the classes to implement different interfaces based on the need
D (Dependency inversion) principle: we should always use a design pattern known as dependency inversion pattern, which is usually resolved by the use of dependency injection
Any software backend code that proceeds with the recommendations of the S.O.L.I.D. principles are much easier to be shared with collaborators, scaled up to meet volume, modified based on user requirements, tested for stress or usability, and managed and troubleshooted without any issues.
https://www.baeldung.com
https://www.bezkoder.com
https://www.w3schools.com
https://blog.knoldus.com
https://html5boilerplate.com
MORE HERE: IntelliJ IDEA Beginner Tutorial | How to create Maven Project, https://www.youtube.com/watch?v=dxn5DsMWhGY
MORE HERE: https://www.jetbrains.com/help/idea/maven-support.html
MORE HERE: Working With Maven in IntelliJ IDEA, https://www.youtube.com/watch?v=pt3uB0sd5kY
MORE HERE: Maven Tutorial - Crash Course, https://www.youtube.com/watch?v=Xatr8AZLOsE
MORE HERE: https://programiz.pro/learn/master-java?utm_source=right-floating-block&utm_campaign=programiz&utm_medium=referral
MORE HERE: design patterns, https://www.tutorialspoint.com/design_pattern/design_pattern_overview.htm