2016-07-19 6 views
1

У меня возникли проблемы с поиском «пути» для решения проблемы дублирования кода. Вот проблема. Рассмотрим следующий пример:Уменьшение дублирования кода в Golang

type (
    WithKey interface { 
    key() string 
    } 

    SharedFunctionality interface { 
    WithKey 
    MethodA() string 
    MethodB() string 
    // ... etc ... 
    } 

    FirstType struct { ... } 
    SecondType struct { ... } 
    // ... etc ... 
) 
func (ft *FirstType) key() string { ... } 
func (st *SecondType) key() string { ... } 

Теперь методы в SharedFunctionality они зависят только от результатов метода key(). Я мог бы реализовать их как следующее:

func runMethodA(k WithKey) string { 
    key := k.key() 
    // do something and return a string 
} 
func runMethodB(k WithKey) string { 
    key := k.key() 
    // do something else and return a string 
} 

func (ft *FirstType) MethodA() string { return runMethodA(ft) } 
func (ft *FirstType) MethodB() string { return runMethodB(ft) } 
func (st *SecondType) MethodA() string { return runMethodA(st) } 
func (st *SecondType) MethodB() string { return runMethodB(st) } 

Что мне не нравится в этом подходе является то, что, как я добавить больше типов (ThirdType, FourthType, и т.д.) или добавить новые методы SharedFunctionality, я должен добавить тонн кода шаблона ... специально для M-методов в SharedFunctionality и N типах я должен был бы описать однострочные M * N, такие как 4 выше.

Что бы я любовь сделать, это что-то вроде:

func (k WithKey) MethodA() string { 
    key := k.key() 
    // do something 
} 

Другими словами: Я хотел бы определить метод по типу интерфейса. Значение: все объекты, которые реализуют «WithKey», автоматически получат MethodA() string, MethodB() string и т. Д., Поэтому они автоматически реализуют интерфейс SharedFunctionality. Что-то вроде методы по умолчанию в Интерфейсы Java.

Однако, я знаю, что это невозможно определить метод в типе интерфейса ...

Что идти путем решения этой проблемы?

Я видел подход, при котором я бы создать с-структуру анонимного поля типа интерфейса, а затем реализовать методы там:

type SharedFuncStruct struct { 
    WithKey 
} 
func (sfs *SharedFuncStruct) MethodA() string { 
    key := sfs.key() 
    // whatever 
} 
// same for MethodB() 

Затем, чтобы использовать его, я хотел бы сделать что-то вроде:

first := ... getFirstTypeValue() 
sfs := &SharedFuncStruct{first} 
sfs.MethodA() // etc 

Похоже, что это может сработать, но оно все еще похоже на слишком много шаблонов.

Любые другие альтернативы?

+0

Вы в значительной степени описали свои варианты. И см. Ответ Дэйва ниже. И подумайте об этом: что произойдет, если конкретный тип уже будет иметь метод MethodA(), кроме 'key()'? По крайней мере, _embedding_ делает однозначным то, что означало бы 'MethodA()' (на «самой мелкой глубине» _). – icza

+0

Ваш вопрос по существу «как я наследую реализацию реализации и виртуальные методы в go», но я думаю, что, возможно, у вас есть проблема xy. http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem. Если вы описываете небольшой контекст того, что вы делаете, возможно, это решение, которое не связано с объектно-ориентированным дизайном в стиле Java, который лучше подходит для работы. –

ответ

1

Забавный факт: вы можете встроить интерфейсы в структурах, а структура автоматически выполняет этот интерфейс. Вы можете использовать это, чтобы эффективно определять методы интерфейсов:

https://play.golang.org/p/ZufTOzr9ig

type (
    WithKey interface { 
     key() string 
    } 

    SharedFunctionality interface { 
     WithKey 
     MethodA() string 
     MethodB() string 
    } 

    KeyHolder struct { 
     WithKey 
    } 

    FirstType struct { ... } 
    SecondType struct { ... } 
) 

func (k *KeyHolder) MethodA() string { 
    key := k.key() 
    // ... 
} 

func (k *KeyHolder) MethodB() string { 
    key := k.key() 
    // ... 
} 

func NewSharedFunctionality(w WithKey) SharedFunctionality { 
    return &KeyHolder{w} 
} 

func (ft *FirstType) key() string { ... } 
func (st *SecondType) key() string { ... } 

В этом случае KeyHolder структура вкладывается интерфейс WithKey, и, таким образом, может держать что-нибудь, что имеет key() string метод (который, как FirstType и SecondType есть).Затем вы можете определить MethodA и MethodB на этой структуре, и тогда структура будет выполнять как интерфейс WithKey (поскольку он внедряет его), так и интерфейс SharedFunctionality, используя любой ключ, который возвращается встроенным WithKey.

Другими словами, вместо оберточной FirstType в WithKey, а затем в SharedFunctionality (то есть FirstType сам должен определить key(), MethodA() и MethodB()), вы заключаете FirstType в WithKey, а затем вставлять его (как интерфейс WithKey) в некоторых другая структура, которая существует только для определения тех методов по умолчанию MethodA и MethodB, которые затем выполняют интерфейс SharedFunctionality.

+0

Мне нравится этот подход. Мне все равно, что я должен набрать * больше *, чем я ожидал бы (т. Е. Выполняя 'NewSharedFunctionatily (firstTypeInstance)', поэтому я могу назвать свой '.MethodA()'), но похоже, что это лучшее, что я получу. Мне также нравится подход Дейва, но, по крайней мере, на данный момент кажется, что это решение работает лучше для меня (т. Е. Имеет объект, реализующий методы, вместо того, чтобы вызывать функции на пакете). Благодаря! –

2

Мне кажется, что вам нужно извлечь пакет. То, как я бы функции является

package keyed 

type hasKey interface { 
    Key() string 
} 

func MethodA(k hasKey) string { 
    key := k.Key() 
    // whatever 
} 

func MethodB(k hasKey) string { 
    key := k.Key() 
    // whatever 
} 

, а затем

package your_package 

import "keyed" 

type (
    FirstType struct { ... } 
    SecondType struct { ... } 
) 

func (ft *FirstType) Key() string { ... } 
func (st *SecondType) Key() string { ... } 

func main() { 
    first := &FirstType{} 
    second := &SecondType{} 
    keyed.MethodA(first) 
    keyed.MethodA(second) 
    keyed.MethodB(first) 
    keyed.MethodB(second) 
} 
+0

спасибо! Это определенно уменьшает количество кода шаблона. Я обязательно подумаю о создании пакетов для определенных сценариев. Для конкретного сценария, с которым я сейчас работаю, предложение Кеедиса кажется немного лучше - я приму его решение; но спасибо за ваше решение, а также, очень элегантно и полезно. +1! –

 Смежные вопросы

  • Нет связанных вопросов^_^