在Bash中模拟OOP | Simulating OOP in Bash

Made by Mike_Zhang


Unfold Linux Topics | 展开Linux主题 >

Unfold Java Topics | 展开Java主题 >


Intro

导言

众所周知,Java对面向对象编程(Object-Oriented Programming, OOP)的支持非常好。因此,我特别喜欢Java这门编程语言,配合OOP,可以十分快速和规范的对问题进行抽象和建模。

最近在学习Shell Script,这是一门结构很直接的语言,在Shell中运行。其支持十分典型的面相过程编程 (Process-Oriented Programming, POP),并不支持OOP。但是在用Shell Script处理一些复杂问题时,直接的POP并不能很好的对问题进行抽象和建模。因此,我想尝试用Shell Script来实现模拟OOP。

受限于Shell Script的语法,本文只会模拟一下OOP的基础特性,欢迎大家在下方交流。

本文使用Bash编写Shell Script。


1. Field Abstraction

1. 属性抽象

属性(Field)作为对象的静态成员变量,用来描述对象的状态。

在Java中,一般用以下方式对属性进行抽象:

1
2
3
4
public class Book{ 
private String bookName;
private int bookAmount;
}

但是在Shell Script中,没有类的概念,因此我们不妨用函数来模拟类,比如:

1
2
3
Book() {

}

然后在函数中用变量模拟类的属性,比如:

1
2
3
4
5
Book() {
self=$1
bookName=$2
bookAmount=$3
}

注意:

  • self为当前对象的对象名,用来区分不同的对象,类似Python中的self
  • bookNamebookAmount为对象的属性;

以上我们完成了对对象属性的抽象,接下来我们需要对对象的行为进行抽象。


2. Method Abstraction

2. 方法抽象

方法定义了此类实例的行为,能够对此类实例进行的操作。

2.1 Constructor

2.1 构造方法

类的构造方法(Constructor)是一种的特殊的方法, 每次用类实例化一个对象时, 构造方法会自动调用。

在Java中,构造方法的例子如下:

1
2
3
4
5
6
7
8
9
10
public class Book{ 
private String bookName;
private int bookAmount;

// Constructor
public Book(String inName, int inAmount){
bookName = inName;
bookAmount = inAmount;
}
}

在Java实例化对象时,我们通常用以下语句:

1
Book myBook = new Book("Harry Potter", 300);

在Shell Script中,我们可以用以下语句来模拟Java中的构造方法:

1
2
3
4
5
Book() {
self=$1
bookName=$2
bookAmount=$3
}
  • 没错,这与上文模拟属性的代码是一样的,同时完成了构造方法的模拟;

并用以下语句来模拟Java中的实例化对象:

1
2
# Create a new book object, object name is myBook
Book myBook "Harry Potter" 300

对比Java中:

1
Book myBook = new Book("Harry Potter", 300);

对比看出,用Shell Script的函数来模拟类,可以很好的“实现”Java中的构造方法。


完成了构造方法的模拟,接下来我们当然还要模拟一下Java中经典的gettersetter方法。


2.2 Getter Method

2.2 访问器方法

访问器方法是用来访问并获取对象的属性值的方法,在Java中如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Book{ 
private String bookName;
private int bookAmount;

// ...

// Getter method
// Get book name
public String getBookName(){
return bookName;
}

// Get book amount
public int getBookAmount(){
return bookAmount;
}
}

并可以通过以下语句来调用:

1
2
myBook.getBookName();
myBook.getBookAmount();

在Shell Script中,我们可以用环境变量来巧妙的模拟getter方法,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Book() {
self=$1
bookName=$2
bookAmount=$3

# ...

# Getter method
# Get book name
export ${self}_getBookName="$bookName"

# Get book amount
export ${self}_getBookAmount="$bookAmount"
}

并可以通过以下语句来调用:

1
2
$myBook_getBookName
$myBook_getBookAmount

对比Java:

1
2
myBook.getBookName();
myBook.getBookAmount();
  • 形式上十分相似;
  • 但在本质上不同,Java为调用方法,而Shell Script只是访问环境变量;

2.3 Setter Method

2.3 修改器方法

修改器方法是用来修改对象的属性值的方法,在Java中如:

1
2
3
4
5
6
7
8
9
10
11
12
public class Book{ 
private String bookName;
private int bookAmount;

// ...

// Setter method
// Set book name
public void setBookName(String inName){
bookName = inName;
}
}

并可以通过以下语句来调用:

1
myBook.setBookName("Lord of the Rings");

在Shell Script中,我们可以用其函数的模拟setter方法,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Book() {
self=$1
bookName=$2
bookAmount=$3

# ...

# Setter method
# set book name
setBookName() {
object=$1
name=$2
export ${object}_getBookName="$name"
}
}

并可以通过以下语句来调用:

1
setBookName myBook "The Lord of the Rings"

对比Java:

1
myBook.setBookName("Lord of the Rings");
  • 可以发现十分类似;
  • 都有对象名和参数值,都调用了函数或方法;

2.4 General Method

2.4 一般方法

我们还可以尝试一些其他的方法,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Book{ 
// ...

// Increase book amount
public void increaseBookAmount(int inAmount){
bookAmount += inAmount;
}

// Decrease book amount with error checking and handling
public void decreaseBookAmount(int inAmount){
if (bookAmount - inAmount < 0){
System.out.println("Error: Not enough books to decrease");
}
else {
bookAmount -= inAmount;
}
}
}

用Shell Script来模拟:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Book() {
# ...

# Increase book amount
increaseBookAmount() {
object=$1
amount=$2
cmd1=${object}_getBookAmount
oldAmount=${!cmd1} # to execute the cmd1 variable
newAmount=$(($oldAmount + $amount))
export ${object}_getBookAmount="$newAmount"
}

# Decrease book amount with error checking and handling
decreaseBookAmount() {
object=$1
amount=$2
cmd1=${object}_getBookAmount
oldAmount=${!cmd1} # to execute the cmd1 variable
newAmount=$(($oldAmount - $amount))
if [ $newAmount -lt 0 ]; then
echo "Error: Not enough books to decrease"
else
export ${object}_getBookAmount="$newAmount"
fi
}
}

分别调用:

Java:

1
2
3
myBook.increaseBookAmount(100);
myBook.decreaseBookAmount(200);
myBook.decreaseBookAmount(300);

Bash:

1
2
3
increaseBookAmount myBook 100
decreaseBookAmount myBook 200
decreaseBookAmount myBook 300

3. Code Example

3. 代码示例

以下为完整代码和运行示例:

Java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class Book{ 
private String bookName;
private int bookAmount;

// Constructor
public Book(String inName, int inAmount){
bookName = inName;
bookAmount = inAmount;
}

// Getter method
public String getBookName(){
return bookName;
}

public int getBookAmount(){
return bookAmount;
}

// Setter method
public void setBookName(String inName){
bookName = inName;
}

// Increase book amount
public void increaseBookAmount(int inAmount){
bookAmount += inAmount;
}

// Decrease book amount with error checking and handling
public void decreaseBookAmount(int inAmount){
if (bookAmount - inAmount < 0){
System.out.println("Error: Not enough books to decrease");
}
else {
bookAmount -= inAmount;
}
}

public static void main(String[] args){
Book myBook = new Book("Harry Potter", 300);
System.out.println(myBook.getBookName()); // Harry Potter
System.out.println(myBook.getBookAmount()); // 300
myBook.setBookName("Lord of the Rings");
System.out.println(myBook.getBookName()); // Lord of the Rings
myBook.increaseBookAmount(100);
System.out.println(myBook.getBookAmount()); // 400
myBook.decreaseBookAmount(200);
System.out.println(myBook.getBookAmount()); // 200
myBook.decreaseBookAmount(300); // Error: Not enough books to decrease
}
}

Bash:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#!/bin/bash

# Book myBook "The Hobbit" 300
Book() {
# Field and constructor
self=$1
bookName=$2
bookAmount=$3

# Getter method
export ${self}_getBookName="$bookName"
export ${self}_getBookAmount="$bookAmount"

# Setter method
# e.g. setBookName myBook "The Lord of the Rings"
setBookName() {
object=$1
name=$2
export ${object}_getBookName="$name"
}

# Increase book amount
# e.g. increaseBookAmount myBook 100
increaseBookAmount() {
object=$1
amount=$2
cmd1=${object}_getBookAmount
oldAmount=${!cmd1} # to execute the cmd1 variable
newAmount=$(($oldAmount + $amount))
export ${object}_getBookAmount="$newAmount"
}

# Decrease book amount with error checking and handling
# e.g. decreaseBookAmount myBook 100
decreaseBookAmount() {
object=$1
amount=$2
cmd1=${object}_getBookAmount
oldAmount=${!cmd1} # to execute the cmd1 variable
newAmount=$(($oldAmount - $amount))
if [ $newAmount -lt 0 ]; then
echo "Error: Not enough books to decrease"
else
export ${object}_getBookAmount="$newAmount"
fi
}
}

Book myBook "Harry Potter" 300
echo $myBook_getBookName # Harry Potter
echo $myBook_getBookAmount # 300
setBookName myBook "The Lord of the Rings"
echo $myBook_getBookName # The Lord of the Rings
increaseBookAmount myBook 100
echo $myBook_getBookAmount # 400
decreaseBookAmount myBook 200
echo $myBook_getBookAmount # 200
decreaseBookAmount myBook 300 # Error: Not enough books to decrease

Outro

尾巴

本文简单地用Shell Script模拟了Java中的OOP,但只模拟了一些基础特性。OOP的更多经典特性并未体现,如继承、多态、权限符、抽象类、接口等。或许可以尝试用Shell Script来模拟这些特性,我相信会非常有趣,并且会对面向对象编程用更深的理解,也是对Shell Script的练习。


References

参考资料

Hipersayan_x, “The Samurai Code: Object Oriented Programming in Bash,” The Samurai Code, Dec. 22, 2012. https://hipersayanx.blogspot.com/2012/12/object-oriented-programming-in-bash.html (accessed Feb. 07, 2023).

“Simulating OOP in Bash,” DEV Community. https://dev.to/leandronsp/simulating-oop-in-bash-3mop (accessed Feb. 07, 2023).


原创文章,转载请标明出处
Made by Mike_Zhang




感谢你的支持

在Bash中模拟OOP | Simulating OOP in Bash
https://ultrafish.io/post/oop-in-bash/
Author
Mike_Zhang
Posted on
February 7, 2023
Licensed under