Tutorial

Написание пакетов в Go

Published on January 24, 2020
Русский
Написание пакетов в Go

Пакет состоит из файлов Go, которые находятся в том же каталоге и имеют одинаковое выражение пакета в начале. Вы можете добавить дополнительные функции из пакетов, чтобы усложнить программы. Некоторые пакеты доступны в стандартной библиотеке Go и устанавливаются вместе с Go. Другие можно установить с помощью команды go get в Go. Также вы можете создавать собственные пакеты Go, создавая файлы Go в том же каталоге, где вы хотите разместить общий код, используя необходимое выражение пакета.

В этом обучающем руководстве вы научитесь писать пакеты Go для использования в других программных файлах.

Предварительные требования

  • Настройте среду программирования Go в соответствии с указаниями одного из обучающих руководств серии Установка и настройка локальной среды программирования Go. Создайте рабочее пространство Go, следуя указаниям шага 5 обучающих руководств по локальной среде программирования. Чтобы следовать примерам и правилам присвоения имен из этой статьи, прочитайте первый раздел, посвященный написанию и импорту пакетов.
  • Чтобы узнать больше о GOPATH, прочитайте нашу статью Знакомство с GOPATH.

Написание и импорт пакетов

Пакеты пишутся так же, как любые другие файлы Go. Пакеты могут содержать определения функций, типов и переменных, которые могут использоваться в других программах Go.

Перед созданием нового пакета необходимо перейти в рабочее пространство Go. Обычно его можно найти по адресу gopath. Например, в этом обучающем модуле мы назовем пакет greet. Для этого мы создали каталог с именем greet с путем gopath в рабочем пространстве проекта. Допустим наша организация называется gopherguides, и мы хотим создать пакет greet в каталоге организации, используя Github в качестве хранилища кода. В этом случае наш каталог будет выглядеть следующим образом:

└── $GOPATH
    └── src
        └── github.com
            └── gopherguides

Каталог greet находится внутри каталога gopherguides:

└── $GOPATH
    └── src
        └── github.com
            └── gopherguides
                └── greet

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

└── $GOPATH
    └── src
        └── github.com
            └── gopherguides
                └── greet
                    └── greet.go

После создания файла мы можем начать писать код для повторного использования или совместного использования в нескольких проектах. В данном случае мы создадим функцию с именем Hello, которая выводит текст Hello World.

Откройте файл greet.go в текстовом редакторе и добавьте следующий код:

greet.go
package greet

import "fmt"

func Hello() {
	fmt.Println("Hello, World!")
}

Разделим первый файл на части. В первой строке каждого файла нужно указать имя пакета, с которым вы работаете. Поскольку вы находитесь в пакете greet, вы используете ключевое слово package, за которым идет имя пакета:

package greet

Так компилятор будет рассматривать все содержание файла как часть пакета greet.

Затем вы декларируете все другие пакеты, которые вам нужно использовать, с помощью выражения import. В этом файле вы используете только пакет fmt:

import "fmt"

В заключение вы создадите функцию Hello. Она будет использовать пакет fmt для вывода текста Hello, World!:

func Hello() {
	fmt.Println("Hello, World!")
}

Теперь вы записали пакет greet и можете использовать его в любом другом создаваемом вами пакете. Создадим новый пакет, в котором будет использоваться ваш пакет greet.

Вы создадите пакет с именем example, и это означает, что вам требуется каталог с именем example. Создайте пакет в организации gopherguides, чтобы структура каталогов выглядела следующим образом:

└── $GOPATH
    └── src
        └── github.com
            └── gopherguides
                    └── example

Теперь у вас есть каталог нового пакета, и вы можете создать файл точки входа. Поскольку это исполняемая программа, следует присвоить файлу точки входа имя main.go:

└── $GOPATH
    └── src
        └── github.com
            └── gopherguides
                └── example
                    └── main.go

Откройте в текстовом редакторе файл main.go и добавьте следующий код для вызова пакета greet:

main.go
package main

import "github.com/gopherguides/greet"

func main() {
	greet.Hello()
}

Поскольку вы импортируете пакет, вам нужно вызвать функцию, сославшись на имя пакета в точечной нотации. Точечная нотация — это практика, предусматривающая постановку точки . между именем используемого пакета и ресурсом этого пакета, который вы хотите использовать. Например, в пакете greet вы можете использовать функцию Hello в качестве ресурса. Если вы хотите вызвать этот ресурс, вы можете использовать точечную нотацию greet.Hello().

Теперь вы можете открыть терминал и запустить команду из командной строки:

  1. go run main.go

В этом случае вы получите следующий результат:

Output
Hello, World!

Чтобы посмотреть, как использовать переменные в пакете, добавим определение переменной в файл greet.go:

greet.go
package greet

import "fmt"

var Shark = "Sammy"

func Hello() {
	fmt.Println("Hello, World!")
}

Откройте файл main.go и добавьте следующую выделенную строку для вызова переменной из greet.go в функции fmt.Println():

main.go
package main

import (
	"fmt"

	"github.com/gopherguides/greet"
)

func main() {
	greet.Hello()

	fmt.Println(greet.Shark)
}

После повторного запуска программы:

  1. go run main.go

Вывод должен выглядеть следующим образом:

Output
Hello, World! Sammy

В заключение определим тип в файле greet.go. Вы создадите тип Octopus с полями name и color, а также функцию, которая будет выводить поля при вызове:

greet.go
package greet

import "fmt"

var Shark = "Sammy"

type Octopus struct {
	Name  string
	Color string
}

func (o Octopus) String() string {
	return fmt.Sprintf("The octopus's name is %q and is the color %s.", o.Name, o.Color)
}

func Hello() {
	fmt.Println("Hello, World!")
}

Откройте файл main.go для создания экземпляра этого типа в конце файла:

main.go
package main

import (
	"fmt"

	"github.com/gopherguides/greet"
)

func main() {
	greet.Hello()

	fmt.Println(greet.Shark)

	oct := greet.Octopus{
		Name:  "Jesse",
		Color: "orange",
	}

	fmt.Println(oct.String())
}

После создания экземпляра типа Octopus с помощью выражения oct := greet.Octopus вы сможете получить доступ к функциям и полям типа в пространстве имен файла main.go. Это позволит вам записать oct.String() в последнюю строку без вызова greet. Например, вы можете вызвать одно из полей типа, например oct.Color, не ссылаясь на имя пакета greet.

Метод String типа Octopus использует функцию fmt.Sprintf для создания предложения и возвращает результат в виде строки вызывающему компоненту (в данном случае, основной программе).

При запуске программы вы получите следующий результат:

  1. go run main.go
Output
Hello, World! Sammy The octopus's name is "Jesse" and is the color orange.

Создавая метод String в типе Octopus, вы получаете возможность многократно выводить информацию о своем пользовательском типе. Если вы захотите изменить поведение этого метода, вам нужно будет только отредактировать этот метод.

Экспортированный код

Возможно вы заметили, что все декларации в вызванном вами файле greet.go были указаны заглавными буквами. В Go отсутствует концепция модификаторов public, private и protected, как в других языках. Видимость для внешних элементов определяется использованием заглавных букв. Типы, переменные, функции и другие элементы, которые начинаются с заглавной буквы, находятся в публичной зоне видимости. Символ, видимый вне пакета, считается экспортируемым.

Если вы добавите в Octopus новый метод с именем reset, вы можете вызвать его из пакета greet, но не из файла main.go, который находится вне пакета greet:

greet.go
package greet

import "fmt"

var Shark = "Sammy"

type Octopus struct {
	Name  string
	Color string
}

func (o Octopus) String() string {
	return fmt.Sprintf("The octopus's name is %q and is the color %s.", o.Name, o.Color)
}

func (o *Octopus) reset() {
	o.Name = ""
	o.Color = ""
}

func Hello() {
	fmt.Println("Hello, World!")
}

Если вы попробуете вызвать метод reset из файла main.go:

main.go
package main

import (
	"fmt"

	"github.com/gopherguides/greet"
)

func main() {
	greet.Hello()

	fmt.Println(greet.Shark)

	oct := greet.Octopus{
		Name:  "Jesse",
		Color: "orange",
	}

	fmt.Println(oct.String())

	oct.reset()
}

Вы получите следующую ошибку компиляции:

Output
oct.reset undefined (cannot refer to unexported field or method greet.Octopus.reset)

Чтобы выполнить экспорт функции reset из Octopus, нужно сделать заглавной букву R в имени метода reset:

greet.go
package greet

import "fmt"

var Shark = "Sammy"

type Octopus struct {
	Name  string
	Color string
}

func (o Octopus) String() string {
	return fmt.Sprintf("The octopus's name is %q and is the color %s.", o.Name, o.Color)
}

func (o *Octopus) Reset() {
	o.Name = ""
	o.Color = ""
}

func Hello() {
	fmt.Println("Hello, World!")
}

В результате вы можете вызвать метод Reset из другого пакета без сообщения об ошибке:

main.go
package main

import (
	"fmt"

	"github.com/gopherguides/greet"
)

func main() {
	greet.Hello()

	fmt.Println(greet.Shark)

	oct := greet.Octopus{
		Name:  "Jesse",
		Color: "orange",
	}

	fmt.Println(oct.String())

	oct.Reset()

	fmt.Println(oct.String())
}

Теперь, если вы запустите программу:

  1. go run main.go

Результат будет выглядеть следующим образом:

Output
Hello, World! Sammy The octopus's name is "Jesse" and is the color orange The octopus's name is "" and is the color .

Вызывая метод Reset, вы очистили всю информацию в полях Name и Color. При вызове метода String он не будет выводить никакого текста, где появляются Name и Color, поскольку теперь эти поля пустые.

Заключение

Написание пакета Go аналогично написанию любого другого файла Go, однако его размещение в другом каталоге позволяет изолировать код для повторного использования в любом месте. В этом обучающем руководстве мы показали, как написать определения пакета, продемонстрировали использование этих определений в другом файле Go и описали варианты хранения пакета для доступа к нему.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


About the authors


Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel