Golang实现两种FTP连接的组织方式:通用方法与特定方法
Golang实现两种FTP连接的组织方式:通用方法与特定方法
我有两个FTP连接(FTP1,FTP2)。每个连接都有一些共同的实现(如 connect()、download() 等),以及特定的实现(例如,FTP1 中的文件格式为 .gzip,FTP2 中的文件格式为 .zip,因此有特定的 unzip() 方法)。
我希望能够像这样调用它们:
func main() {
var acqs []Acquisition
ftp, err := NewFTP(os.Getenv("FTP_USER"), os.Getenv("FTP_PASSWORD"), os.Getenv("FTP_HOST"), os.Getenv("FTP_PORT"))
if err != nil {
panic(err)
}
ftp1 := &FTP1{*ftp}
ftp2 := &FTP2{*ftp}
acqs = append(acqs, ftp1, ftp2)
for _, acq := range acqs {
meters, err := acq.FetchMeters()
if err != nil {
log.Warn(acq.Name(), " got error :", err)
}
log.Info(meters)
}
}
其中:
type Acquisition interface {
FetchMeters() ([]Meter, error)
Name() string
}
type Meter struct {
ID string
OperationID string
Unit string
}
type FTP struct {
Username string
Password string
Url string
Port string
client *goftp.FTP
}
type FTP1 struct {
FTP
}
type FTP2 struct {
FTP
}
func NewFTP(user, password, url, port string) (*FTP, error) {
var err error
ftp := &FTP{
Username: user,
Password: password,
Url: url,
Port: port,
}
if ftp.client, err = goftp.Connect(url + ":" + port); err != nil {
return nil, err
}
if err = ftp.client.Login(user, password); err != nil {
return nil, err
}
return ftp, nil
}
对于所有FTP连接,FetchMeters() 方法将是:
func (ftp FTP) FetchMeters() ([]Meter, error) {
log.Info(ftp.Name(), " is running")
file := ftp.Download("")
file = ftp.Unzip("") // 我有多个 Unzip 的实现
log.Info(file)
return nil, nil
}
其中 Download() 是所有 FTP 结构体共有的,但 Unzip() 是 ftp1 和 ftp2 特有的。
当然,在我的例子中,FTP 类型并没有 Unzip() 方法,所以会失败。
我应该做的是将接收器设置为一个抽象类型,即接口 FTPAcq:
type FTPAcq interface {
Unzip(file string) string
}
func (ftp FTPAcq) FetchMeters() ([]Meter, error) {
...
}
但编译器报错了。
我花了很长时间思考应该如何做,这看起来很简单,但我无法理解我遗漏了什么。
以下是完整的代码:
更多关于Golang实现两种FTP连接的组织方式:通用方法与特定方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html
JuSun:
但是编译器报错了。
关于什么?
更多关于Golang实现两种FTP连接的组织方式:通用方法与特定方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
这正是我想要实现的目标,但我却做不到 😞
也许传递一个实现了 FTP 接口的类型实例作为参数?
JuSun:
g
它显示:
invalid receiver type FTPAcq (FTPAcq is an interface type)
所以你想在接口 FTPAcq 上实现方法 FetchMeters?
不,你尝试在接口上实现一个方法,这与传递参数不同。也许这样?
func FetchMeters(ftp FTPAcq) ([]Meter, error)
不,在FetchMeters中,我希望能够使用 file = ftp.Unzip(""),但 Unzip() 在FTP1和FTP2上有不同的实现。
是的,但现在,我该如何调用 Unzip() 而无需调用 ftp1.Unzip() 或 ftp2.Unzip() 呢?
我希望有一个方法,它不关心自己管理的是哪种类型的 ftp,然后调用 ftp.Unzip(),这个调用应该能将代码重定向到正确的 Unzip() 实现。
嗯,编译器很可能在抱怨这个:
func (ftp FTPAcq) FetchMeters() ([]Meter, error) {
你试图在接口 FTPAcq 上实现方法 FetchMeters,这是没有意义的。在 Go 语言中,接口定义的是方法签名,而结构体类型负责实现它们。
删除
func (ftp FTPAcq) FetchMeters() ([]Meter, error) {
...
}
关于你的问题:你的接口 FTPAcq 定义了一个方法 Unzip(file string) string。如果你想为 FTP1 和 FTP2 以不同的方式实现这个方法,可以这样做:
func (ftp1 FTP1) Unzip(file string) string {
// 实现 1
}
func (ftp2 FTP2) Unzip(file string) string {
// 实现 2
}
我可以这样做:
func (acq FTPAcq) FetchMeters(ftp FTP) ([]Meter, error) {
log.Info(ftp.Name(), " is running")
file := Download("")
file = acq.Unzip("") // 我有多个Unzip的实现
log.Info(file)
return nil, nil
}
但现在,meters, err := acq.FetchMeters() 的签名与接口不匹配。
需要说明的是,我还有另一种采集类型:API。为了简化,我省略了它,但FetchMeters()将根据它是API还是FTP有两种不同的实现。然后我有多个API(API1、API2)和FTP(FTP1、FTP2)的实现。
在写作过程中,我已经尝试了100种不同的方法,但找不到一种适用于所有情况的方法。
ftp.FetchMeters() 和 api.FetchMeters() 必须有两种不同的实现。ftp 将满足 FTPAcq 接口,而 api 将满足 APIAcq 接口。
type FTPAcq interface {
Unzip(file string) string
}
type APIAcq interface {
OtherMethod(file string) string
}
如你所见,我有点迷茫了。
在Go中,可以通过接口组合和嵌入来实现这种模式。以下是完整的解决方案:
package main
import (
"fmt"
"log"
)
// Meter 结构体
type Meter struct {
ID string
OperationID string
Unit string
}
// Acquisition 接口
type Acquisition interface {
FetchMeters() ([]Meter, error)
Name() string
}
// FTP 基础结构体
type FTP struct {
Username string
Password string
Url string
Port string
client interface{} // 模拟FTP客户端
}
// FTPAcq 接口,定义特定方法
type FTPAcq interface {
Acquisition
Unzip(file string) string
Download(file string) string
}
// FTP1 结构体
type FTP1 struct {
FTP
}
// FTP2 结构体
type FTP2 struct {
FTP
}
// NewFTP 创建基础FTP连接
func NewFTP(user, password, url, port string) (*FTP, error) {
ftp := &FTP{
Username: user,
Password: password,
Url: url,
Port: port,
client: "connected",
}
return ftp, nil
}
// Download 通用方法
func (ftp *FTP) Download(file string) string {
return fmt.Sprintf("Downloaded file from %s:%s", ftp.Url, ftp.Port)
}
// Name 方法
func (ftp *FTP) Name() string {
return "BaseFTP"
}
// FTP1 的特定实现
func (ftp1 *FTP1) Unzip(file string) string {
return "Unzipped with .gzip format"
}
func (ftp1 *FTP1) FetchMeters() ([]Meter, error) {
log.Printf("%s is running", ftp1.Name())
file := ftp1.Download("")
unzipped := ftp1.Unzip(file)
log.Printf("FTP1: %s", unzipped)
return []Meter{
{ID: "FTP1-001", OperationID: "OP1", Unit: "kWh"},
}, nil
}
func (ftp1 *FTP1) Name() string {
return "FTP1"
}
// FTP2 的特定实现
func (ftp2 *FTP2) Unzip(file string) string {
return "Unzipped with .zip format"
}
func (ftp2 *FTP2) FetchMeters() ([]Meter, error) {
log.Printf("%s is running", ftp2.Name())
file := ftp2.Download("")
unzipped := ftp2.Unzip(file)
log.Printf("FTP2: %s", unzipped)
return []Meter{
{ID: "FTP2-001", OperationID: "OP2", Unit: "m³"},
}, nil
}
func (ftp2 *FTP2) Name() string {
return "FTP2"
}
func main() {
var acqs []Acquisition
// 创建基础FTP连接
ftp, err := NewFTP("user", "password", "host", "21")
if err != nil {
panic(err)
}
// 创建特定FTP实例
ftp1 := &FTP1{*ftp}
ftp2 := &FTP2{*ftp}
acqs = append(acqs, ftp1, ftp2)
for _, acq := range acqs {
meters, err := acq.FetchMeters()
if err != nil {
log.Printf("%s got error: %v", acq.Name(), err)
}
log.Printf("Meters: %v", meters)
}
}
或者使用更简洁的接口组合方式:
package main
import (
"fmt"
"log"
)
// Unzipper 解压接口
type Unzipper interface {
Unzip(file string) string
}
// Downloader 下载接口
type Downloader interface {
Download(file string) string
}
// FTP 基础结构体实现通用方法
type FTP struct {
Username string
Password string
Url string
Port string
}
func (ftp *FTP) Download(file string) string {
return fmt.Sprintf("Downloaded from %s", ftp.Url)
}
// FTP1 实现
type FTP1 struct {
*FTP
}
func (f *FTP1) Unzip(file string) string {
return "GZIP unzipped"
}
func (f *FTP1) FetchMeters() ([]Meter, error) {
file := f.Download("")
unzipped := f.Unzip(file)
log.Printf("FTP1 processed: %s", unzipped)
return []Meter{{ID: "1"}}, nil
}
func (f *FTP1) Name() string {
return "FTP1"
}
// FTP2 实现
type FTP2 struct {
*FTP
}
func (f *FTP2) Unzip(file string) string {
return "ZIP unzipped"
}
func (f *FTP2) FetchMeters() ([]Meter, error) {
file := f.Download("")
unzipped := f.Unzip(file)
log.Printf("FTP2 processed: %s", unzipped)
return []Meter{{ID: "2"}}, nil
}
func (f *FTP2) Name() string {
return "FTP2"
}
// 工厂函数
func NewFTP1(ftp *FTP) *FTP1 {
return &FTP1{FTP: ftp}
}
func NewFTP2(ftp *FTP) *FTP2 {
return &FTP2{FTP: ftp}
}
这样每个FTP类型都实现了自己的Unzip()方法,同时通过嵌入FTP结构体继承了通用的Download()方法。

