Go使用内嵌类型实现额外保护——以防盗门示例

本文通过防盗门的生产和使用场景,介绍了Go语言如何利用自定义类型和内嵌类型实现安全性和功能性的增强。具体包括防盗门结构的设计、功能实现及代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


Go语言支持用户自定义类型,而自定义类型中还可以再嵌入其他自定义的类型,被嵌入的类型被称为“内嵌类型/内部类型”,而嵌入内部类型的就被成为“外部类型”。

通过嵌入类型,与内部类型相关的字段、方法、标识符等都会被外部类型所拥有,就像外部类型自己的一样,这样就达到了代码快捷复用组合的目的。

然而 内嵌类型的存在,也让 共享pkg包中某个类型值,但禁止对该类型值进行复制(创建新地址、重新指向),仅以共享的方式传递 成为可能。 这句总结可以说是十分抽象。下面将防盗门的使用这一常见场景来进行示例讲解:


一、需求分析

防盗门的生产和使用场景:

  1. 厂商可以生产防盗门,交到客户手中(安装门)时也会把门的密码告诉客户
  2. 创建的防盗门初始密码都是不相同的
  3. 客户输入密码,密码正确的话可以打开防盗门
  4. 客户修改密码,输入新旧密码,密码正确的话可以修改防盗门密码为新密码
  5. 客户关上防盗门

在设计功能之前,需要先思考下:

防盗门的本质是什么?门。门的话,肯定就能够“开门”和“关门”,但是如果直接由客户来操作门,显然没有任何的安全性。这里必须由防盗门来操作门,而客户只能够操作防盗门,从而保证了安全。门这一本质也由防盗门包含


二、功能设计

根据上面的需求,可以整理出以下功能点:

  • 对外-防盗门(结构:门指针类型,密码)

    • 对外-创建:返回 防盗门指针变量 和其对应的密码
    • 对外-修改密码:输入旧密码和新密码,正确的话将“防盗门”的密码改为新密码
    • 对外-开门:输入密码,正确的话调用“门”的开门方法
    • 对外-关门:此处可以不必实现。因为“门”是“防盗门”的内嵌类型,直接调用“门”的“关门”方法即可
  • 对内-门 (结构:门状态)

    为防止对door类型值进行复制(创建新地址、重新指向),创建防盗门的方法提供给客户,但方法内部初始化防盗门,便是实现“防盗门1用的门1,永远不会被更改使用门2、门3,门1也永远只有防盗门1使用”。

    • 对内-开门:修改门的状态为“开”
    • 对外-关门:修改门的状态为“关”

由需求上分析可知:“门”这一本质也由“防盗门”包含,那么代码实现上便可将door门作为SecurityDoor的内嵌类型。

由于防盗门的修改密码、开门等操作,都是针对“防盗门”本身的,并不是对其复制对象做处理。所以对应操作方法的接收者都是指针接收者。同理,“门”的操作方法也都是指针接收者,且作为SecurityDoor结构的内嵌类型也是传的*door

如果防盗门是可复制的,那我知道防盗门1的密码,再复制防盗门2,这样所有的防盗门密码都是一样,那就可以被乱开了。为防止此类情况发生,防盗门的创建方法便将指针类型直接返回给客户进行处理(也可理解为返回了一个安装在xxx地方的防盗门,相当于定义了防盗门不可复制。就算你想去复制,拿到的也是xxx地方的防盗门,即防盗门1本身,并不是创建了新的防盗门2,只是给防盗门1起了个别名叫做防盗门2)


三、代码实现

代码实现的目录结构如下所示。将“防盗门”和“门”相关功能放在door_factory/door_factory.go文件中,并作为包。main.go文件为模拟客户操作防盗门

在这里插入图片描述

main.go文件:模拟客户进行对防盗门的相关操作

package main

import (
	"door_factory"
	"fmt"
)

func main() {
	//创建一个防盗门,并获得到初始密码
	securityDoor, password := door_factory.CreateSecurityDoor()

	//防盗门是指针类型,指向的地址都是一样的
	securityDoorCopy := securityDoor
	fmt.Printf("securityDoor的地址:%p\n", securityDoor)
	fmt.Printf("securityDoorCopy的地址:%p\n", securityDoorCopy)

	//输入防盗门正确密码开门,并显示结果
	fmt.Println("输入密码打开防盗门:", securityDoor.OpenDoor(password))

	//防盗门关门,使用的door的方法,但由于door是SecurityDoor的内嵌类型,其方法也可被SecurityDoor调用
	fmt.Println("关闭防盗门:", securityDoor.CloseDoor())

	//修改原密码为"1234567890"
	newPassword := "1234567890"
	msg, success := securityDoor.ChangePassword(password, newPassword)
	fmt.Println(msg)

	//如果修改密码成功,再输入旧密码,将会登录失败
	if success {
		//输入防盗门错误密码开门,并显示结果
		fmt.Println("输入旧密码开门:", securityDoor.OpenDoor(password))
	}

	/**
	结果输出:
	securityDoor的地址:0xc000004660
	securityDoorCopy的地址:0xc000004660
	输入密码打开防盗门: 门已打开
	关闭防盗门: 门已关闭
	更新密码成功
	输入旧密码开门: 密码错误
	*/
}


door_factory/door_factory.go文件:

package door_factory

import (
	"bytes"
	"crypto/rand"
	"fmt"
	"math/big"
)

type SecurityDoor struct {
	*door    //door受保护,仅SecurityDoor可被外部调用
	password string
}

//创建一个防盗门
func CreateSecurityDoor() (*SecurityDoor, string) {
	password := createRandomNumber(10)
	securityDoor := SecurityDoor{
		door:     &door{isOpen: false},
		password: password,
	}
	return &securityDoor, password
}

//更改防盗门的密码
func (securityDoor *SecurityDoor) ChangePassword(oldPwd string, newPwd string) (string, bool) {
	//如果密码错误,返回操作错误
	if securityDoor.password != oldPwd {
		return "密码错误", false
	}
	securityDoor.password = newPwd
	return "更新密码成功", true
}

//门,外部无法调用,仅包内可用,但外部可以获得当前门是否打开的状态
type door struct {
	isOpen bool
}

//防盗门开门方法-需要输密码,外部调用
func (securityDoor *SecurityDoor) OpenDoor(password string) string {
	if securityDoor.password != password {
		return "密码错误"
	}
	return securityDoor.door.openDoor()
}

//door开门方法-仅包内可用
func (door *door) openDoor() string {
	if door.isOpen {
		return "门没关,可以直接开"
	}
	door.isOpen = true
	return "门已打开"
}

//door关门方法-外部也可调用
func (door *door) CloseDoor() string {
	if !door.isOpen {
		return "门本来就关着"
	}

	door.isOpen = false
	return "门已关闭"
}

//指定位数随机数
func createRandomNumber(len int) string {
	var numbers = []byte{1, 2, 3, 4, 5, 7, 8, 9}
	var container string
	length := bytes.NewReader(numbers).Len()

	for i := 1; i <= len; i++ {
		random, err := rand.Int(rand.Reader, big.NewInt(int64(length)))
		if err != nil {

		}
		container += fmt.Sprintf("%d", numbers[random.Int64()])
	}
	return container
}


四、总结

  • 为了让客户能够间接操作 “门” ,可以创建一个类似“防盗门”的结构体和对应方法,实现对内嵌类型操作的额外保护

  • 额外保护借助了包的公开私有标识符,如果在同个包,那么也可对door进行声明、初始化、赋值等操作

  • 由于door未公开,在包外无法通过结构字面量初始化该内部类型

  • 如果内嵌类型某方法公开,那么接收者的标识符提升为外部类型,如上面的"关门"操作,同样可以访问。字段也是同理,还可被用于修改

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值