Home  >  Article  >  Backend Development  >  Refactor an object that expects one thing and expects many things

Refactor an object that expects one thing and expects many things

WBOY
WBOYforward
2024-02-08 23:39:08381browse

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!

Question content

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

Workaround

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: &params}, 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!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete