英文:
How to make a model in Go
问题
我想为我的Go框架创建模型,但我不确定如何以一种共享常见数据库交互方法的方式组合它们:保存、更新、删除。
通常我会通过创建一个抽象的Model父类来实现,但Go没有继承。相反,你应该使用嵌入和组合,但我不知道如何嵌入一个模型类并保存它所持有的数据。
我看到另一种选择是创建一个模型类,在其中嵌入一个具体的模型类型,但我真的看不到一个适用于所有模型的接口,除非它是空的。这会带来任何东西都可以被视为模型的不安全性。
怎么办?
英文:
I want to make models for my framework, written in go, and I'm not sure how to compose them in a way that shares the common database interaction methods: save, update, delete.
I would normally do this by creating a Model abstract parent class to all concrete models, but Go doesn't have inheritance. You're supposed to use embedding and composition instead, but I don't see how I can embed a model class and have it save the data of the class holding it.
I see the other option, of creating a model class that embeds a concrete model type within it, but I don't really see an interface that would apply to all the models unless it was empty. That brings with it the insecurity that anything can be considered a model.
What do?
答案1
得分: 4
在我的项目中,我会做类似这样的事情:
type Storable interface {
// 从数据库反序列化后调用
Init() error
// 当对象被删除时调用
// 如果对象需要删除其他对象、更改远程服务器上的状态等,这很有用
Destroy() error
// 在 Init 之后调用,有助于将初始化与
// 检查合法性分开(在使用可能无效的对象之前检测错误很有用)
Validate() error
// 对象的类型,在 `Save` 和 `Update` 中存储在数据库中
// 这样可以在 `Get` 中读取出来
Type() string
}
如果你正在使用 SQL 数据库,你可以这样做:
type Schema map[string]reflect.Type
type SQLStorable interface {
Storable
Schema() Schema
}
然后在数据库中,我有以下函数:
func Get(id string) (Storable, error)
func Save(Storable) error
func Update(id string, Storable) error
func Delete(id string) error
// 向数据库注册一个类型(对应于 Storable 中的 Type())
func Register(typ string, reflect.Type)
我在数据库中保留了一个对象的缓存:map[string]Storable
。这使我能够实现缓存逻辑以减少查找时间(不需要每次从数据库读取时都重新构建对象)。
在我的项目中,我有很多需要与其他包中的对象交互的包。由于管理依赖链将是一场噩梦,所以我建立了一个使用数据库的消息系统:
type Message map[string]interface{}
func Send(id string, Message)
我还在 Storable 中添加了一个 Receive
函数,它接受一个 Message
并返回一个错误。到目前为止,这减少了很多麻烦,并导致了更具可插拔性的设计。
我不确定这是否是“Go 的方式”,但它避免了继承的概念并解决了问题。在数据库逻辑中,我使用了大量的反射来从数据库中获取数据并填充对象。这导致了一些不幸的类型断言,但我想在保持抽象的同时,这是无法避免的。
英文:
In my projects I do something like this:
type Storable interface {
// called after unmarshalling from the database
Init() error
// called when an object is being deleted
// this is useful if the object needs to delete other objects,
// change state on a remote server, etc.
Destroy() error
// called after Init, helps separate initialization from
// sanity checks (useful to detect errors before using a potentially
// invalid object)
Validate() error
// type of this object, stored in the database in `Save` and `Update`
// so it can be read out in `Get`
Type() string
}
If you're working with an SQL database, you could do something like this:
type Schema map[string]reflect.Type
type SQLStorable interface {
Storable
Schema() Schema
}
Then in the database, I have functions like this:
func Get(id string) (Storable, error)
func Save(Storable) error
func Update(id string, Storable) error
func Delete(id string) error
// register a type with the database (corresponds to the Type() in Storable)
func Register(typ string, reflect.Type)
I keep a cache of objects in the database: map[string]Storable
. This allows me to implement caching logic to reduce lookup times (don't need to reconstruct objects each time it's read from the database).
In my project, I have lots of packages that need to talk with objects from other packages. Since managing dependency chains would be a nightmare, I've set up a messaging system that uses the database:
type Message map[string]interface{}
func Send(id string, Message)
And I've added a Receive
function to Storable that takes a Message
and returns an error. This has reduced many headaches so far and has lead to a more pluggable design.
I'm not sure if this is the "Go way", but it avoids the idea of inheritance and solves the problem. In the database logic, I use tons of reflection to grab the data from the database and populate an object with it. It leads to some unfortunate type assertions, but I guess that can't really be helped when trying to keep things abstract.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论