Home >Backend Development >Golang >Refactor an object that expects one thing and expects many things
php Xiaobian Yuzai In programming, we often encounter situations where an object wants to take on multiple responsibilities. This kind of object is called "refactoring an object that expects one thing and expects many things". Such objects usually lead to bloated code, high coupling, and difficulty in maintaining and extending. In this article, we'll explore how to refactor such an object to make it clearer, more flexible, and easier to maintain. Let’s take a look!
I'm working on updating the Go code base. Our company has a central data store, fictionally called the DataStore, that you can publish to and read from various datasets or schemas. Our particular code base has some code that couples specific datasets/schema to the DataStore implementation. I need to refactor this code to be able to write to other (possibly more) datasets.
In the code below, dataStoreSchema
represents a single schema, such as the "Customers" data. But I have other schemas like "Orders", or any schema that has different fields that I want to write to.
transformEvent
Gets the data that will be passed to the DataStore
and performs some logic to create a dataStoreSchema
object and then writes it to the actual DataStore in the cloud.
I want to write to any schema underlying on the data store, not just the schema this implementation is coupled to.
type Parameters struct { SchemaName string Host string Secret string } type DataStore struct { params *Parameters url string request *http.Client } // imagine this defines an Order, but other schemas might be for Customer type dataStoreSchema struct { eventUrl string `json:"url"` objectID string `json:"object_id"` } func New(parameters) (*DataStore, error) { // implementation return &stream, nil } // takes input and converts it into a schema object that can be written to the datastore func (ds *DataStore) transformEvent(...) dataStoreSchema { // .... implementation return dataStoreSchema{ eventUrl: url, objectID: objectId, } } func (ds *DataStore) writeEvents(...) error { // .... implementation payload := ds.transformEvent(event) ds.produce(payload) } func (ds *DataStore) produce(msg []events) { ds.WriteToDatastore(msg) }
The current behavior is like this
myDatasetClient := DataStore.New(params) myDatasetClient.write(<-messages chan)
But I want to be able to do something like this
myDatasetClient1 := DataStore.New(params{schema1, transformEvent1}) myDatasetClient2 := DataStore.New(params{schema2, transformEvent2}) myDatasetClient3 := DataStore.New(params{schema3, transformEvent3})
Or whatever makes the most sense in Go terms
If I understand correctly, your current implementation passes the dataStoreSchema
structure and the transformEvent
Methods are tightly coupled to specific patterns. The transformEvent
method accepts input and converts it into a dataStoreSchema
object, which is then written to the DataStore.
You may wish to write to any schema on the data store, not just the schema to which the current implementation is coupled.
myDatasetClient1 := DataStore.New(params{schema1, transformEvent1}) myDatasetClient2 := DataStore.New(params{schema2, transformEvent2}) myDatasetClient3 := DataStore.New(params{schema3, transformEvent3})
Meaning: You want to create different DataStore
clients, each associated with a different schema and corresponding transformation function (transformEvent1
, transformEvent2
, transformEvent3
).
This should mean decoupling the conversion logic from the DataStore
structure, allowing different conversion functions to be used to handle different schemas.
You can use interfaces to perform different schema conversions and modify the Parameters
and DataStore
structures:
package main import ( "net/http" ) // Define a Transformer function type that takes an event of type E and returns a schema of type S type Transformer[E any, S any] func(event E) S type Parameters[E any, S any] struct { SchemaName string Host string Secret string TransformFn Transformer[E, S] } type DataStore[E any, S any] struct { params *Parameters[E, S] url string request *http.Client } // Define structs for different schemas and events type OrderSchema struct { eventUrl string `json:"url"` objectID string `json:"object_id"` // other fields } type OrderEvent struct { // fields } type CustomerSchema struct { eventUrl string `json:"url"` objectID string `json:"object_id"` // other fields } type CustomerEvent struct { // fields } // New creates a new DataStore func New[E any, S any](params Parameters[E, S]) (*DataStore[E, S], error) { // implementation return &DataStore[E, S]{params: ¶ms}, nil } func (ds *DataStore[E, S]) transformEvent(event E) S { return ds.params.TransformFn(event) } // rest of the code remains the same // Usage: func main() { orderTransformFn := func(event OrderEvent) OrderSchema { // implementation for Order schema } customerTransformFn := func(event CustomerEvent) CustomerSchema { // implementation for Customer schema } myDatasetClient1, _ := New[OrderEvent, OrderSchema](Parameters[OrderEvent, OrderSchema]{SchemaName: "schema1", TransformFn: orderTransformFn}) myDatasetClient2, _ := New[CustomerEvent, CustomerSchema](Parameters[CustomerEvent, CustomerSchema]{SchemaName: "schema2", TransformFn: customerTransformFn}) // ... }The
Transformer function type is now parameterized with two type parameters: E
for event types and S
for schema types.
Parameters and data storage structures are also parameterized using the same two type parameters E
and S
.
The DataStore's transformEvent method now accepts events of type E
and returns a pattern of type S
.
To handle more patterns, you can define new function types or instances that conform to the Transformer
function signature, and the corresponding event and pattern structures. You would not define a new structure that implements the Transformer
interface, since Transformer
is now a function type, not an interface.
Therefore, you can define new transformation functions and schema structures without modifying existing DataStore
implementations.
The above is the detailed content of Refactor an object that expects one thing and expects many things. For more information, please follow other related articles on the PHP Chinese website!