I wrote a lot PHP with the OOP frameworks before, and I'm learning Golang,
When I was using PHP, The class
is useful that I could pass a $model
in to my class then share it between functions:
class User {
function __construct(UserModel $model) {
$this->model = $model
}
function delete($id) {
$this->model->delete($id);
}
function update($id) {
$this->model->update($id);
}
}
$UserModel = new UserModel();
$User = new User($UserModel);
$User->delete(1);
But there's no class
in Golang, I knew that I could treat structs like a class:
type User struct {
model *models.User
}
func (u *User) Delete(id int) {
u.model.Delete(id)
}
func (u *User) Update(id int) {
u.model.Update(id)
}
userModel := &models.User{}
user := User{model: userModel}
user.Delete(1)
I felt like the struct
is used to store the information, and the method of the struct should be used to modify the value of the struct.
But right now I made a struct just because I want to treat it like a class and solve the dependency problem, is it bad to use such approach in Golang?
From overall conceptual standpoint there is nothing wrong in your implementation.
On a detailed view there are inconsistencies that raise questions.
In all cases below there is a potential concurrency issue due to data shared across various Delete calls.
If models.User{} does all the work, then why can't we just
userModel := &models.User{}
userModel.Delete(userId)
If User is our public interface:
user := User{model: userModel}
user.Delete(1)
then taking userId to delete is redundant and shall be done as:
user.Delete()
userId is taken from user context.
Otherwise, we might want to make userModel to be set for all User instances:
package User;
// private package level variable
var model = userModel
func Delete(userId int) {
model.Delete(userId)
}
...
User.Delete(userId)
Each case above solves the same problem with a slightly different emphasis. Case A is straightforward. Case B and C rely on underlying shared code that could be used with event notifications. Case B and C are different in User scope. In Case B User is a structure. In Case C user is a package name. I think Case C is used less often than Case B. So, if use insist on having an underlying model, then Case B is probably the most intuitive solution from user code point of view.
you can use interface with method In service (connect db)
// UserServiceInterface include method list
type UserServiceInterface interface {
GetAll(helpers.ParamsGetAll) (models.PublicUsers, error)
Get(int64) (models.User, error)
Delete(int64) (bool, error)
Create(models.User) (int64, error)
Update(models.User) (models.User, error)
CheckExistUsername(string) (bool, error)
CheckExistEmail(string) (bool, error)
CreateEmailActive(string, string, int64) error
CheckExistUser(int64) (bool, error)
}
// UserService struct
type userService struct{}
// NewUserService to constructor
func NewUserService() userService {
return userService{}
}
func (userService )GetAll (p helpers.ParamGetAll)(models.PublicUser, error) {
code here....
}
In user controller
// UserController controller
type UserController struct {
Service services.UserServiceInterface
}
func (controller UserController) GetAll(c *gin.Context) {
users, errGetAll := controller.Service.GetAll(params)
}