引言
对于我之前关于抽卡系统的描述,大家可能会感到疑惑,并且认为这样单线程一股脑解决方式思路过于呆板,接下来我们讲解一下如何用面向对象的方式对该题进行解答。(有不知道扑克牌比较方式的读者可以看一下之前的抽卡系统)
在写之前,我们要明白面向对象的特征是什么。
初识面向对象
面向对象设计最为典型的三个特征便是封装、继承、多态,
- 封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
- 继承,它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
- 多态,允许将子类类型的指针赋值给父类类型的指针。
今天我们所利用到的主要思想就是封装,那么如何进行封装呢?
封装方法
为了让大家更好的明白和理解封装方法,我从两种角度为大家展开
- 题目解析以及逻辑拆分
我们现在将自己带入一张牌局,你和你的一个朋友在玩一个比较牌大小的游戏。这时一个美女荷官从52张牌中随机发给你们两个人一张牌,这时你们看完自己发的牌后互相比较,最后结果要么是你比你朋友的大,你获得了牌局的胜利,要么是他比你大。
现在我们分析这个事件的主要“人物”——你、你的朋友、荷官、52张扑克牌。你和你的朋友是参与者player,而荷官则是管理和发牌的人PokerManager,然而我们为了让荷官获得每张牌的信息,所以我们需要为52张牌获得应有的属性——数字和花色,然后将这52张牌的属性统统交给荷官,然后由荷官随机抽取这52张牌的两张牌。
大概思路我们从通俗的方式进行了讲解,下面我们从UML图的方式进行讲解。
- UML解释
PokerNumber和PokerSuit则是Poker的两个必要属性,因为每一张牌的属性不同,所以我们必须单独对两个属性进行描述,而不是直接在Poker里面进行添加属性。然后我们在PokerManager里面创建一个专门储存在pokers这个数组里面。必要的Poker所包含的比较方法以及PokerManager中的抽牌方法我们在具体代码中进行描述。
代码分析
- Poker组成属性
- PokerNumber
public class Pokernumber {
public String number;
public int tag;
public Pokernumber(String number, int tag) {
this.number = number;
this.tag = tag;
}
}
2,PokerSuit
public class Pokersuit {
public String suit;
public int tag;
public Pokersuit(String suit, int tag) {
this.suit = suit;
this.tag = tag;
}
}
- Poker
public class Poker {
Pokernumber numberObj;
Pokersuit suitObj;
public Poker(Pokernumber numberObj, Pokersuit suitObj) {
this.numberObj = numberObj;
this.suitObj = suitObj;
}
public Poker() {}
public boolean compare(Poker other)
{
boolean result=true;
if (this.numberObj.tag == other.numberObj.tag)//点数相同比较花色
{
result=this.suitObj.tag > other.suitObj.tag;//利用result返回条件判断结果
} else {
result= this.numberObj.tag > other.numberObj.tag;
}
return result;
}
}
- 荷官 PokerManager
import java.util.ArrayList;
import java.util.Random;
public class PokerManager {
ArrayList<Poker>pokers=new ArrayList<>() ; //为Arraylist开辟空间防止调用时出现无空间情况
String []numbers= {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};
String []suits= {"♦","♣","❤","♠"};
PokerManager(){
for (int i = 0; i < numbers.length; i++) {
for (int s = 0; s < suits.length; s++) {
pokers.add(new Poker(new Pokernumber(numbers[i],i), new Pokersuit(suits[s],s)));
//傻瓜做法
// Poker p1 = new Poker();
// p1.numberObj=new Pokernumber(numbers[i],i);
// p1.suitObj=new Pokersuit(suits[s],s) ;
// pokers.add(p1);
}
}
}
public Poker getPoker()
{
Random rd=new Random();
int index=rd.nextInt(pokers.size());
pokers.remove(index);
return pokers.get(index);
}
}
- 抽牌测试
public class Test {
public static void main(String[] args) {
PokerManager Person1=new PokerManager();
Poker p1=Person1.getPoker();
Poker p2=Person1.getPoker();
if(p1.compare(p2))
{
System.out.println(p1.numberObj.number+p1.suitObj.suit+">"+p2.numberObj.number+p2.suitObj.suit);
}
else {
System.out.println(p1.numberObj.number+p1.suitObj.suit+"<"+p2.numberObj.number+p2.suitObj.suit);
}
}
}
对于我们使用的牌的比较方法可以参考我之前博客https://www.jianshu.com/p/6b70d5a3d0ac
这里我简单描述一下——就是通过自定义字符串集合来规定每一个数或者花色的大小顺序,比较则是比较每个字符串的下标大小。
为什么使用Arraylist
看过之前博客的同学可以看到我之前并没有使用Arraylist这种集合的方式来进行52张牌的存储,而是直接随机抽取,并判断,但是实际上这是并不高效的,并且每一次的条件判断会严重影响程序的真实性——因为我们的实际场景是从52张牌中获得两张牌,而之前的做法相当于从两副牌中抽取分别抽取两张牌,抽取后判断是否相等,这样显然是不符合题意的,因为我们是从一副牌中抽取两张牌的。所以我们用集合的方式储存52张Pokers,并且用remove的方式来解决出现同一张牌——其实跟现实生活中一样,抽取一张牌后,这张牌就会从荷官所管理的一副牌,取出所以我们调用pokers.remove()取出那张牌。
有兴趣的读者可以看一下这个程序的Kotlin版本
- Poker属性
- PokerNumber
- PokerNumber
package KotlinPoker
//点数
class PokerNumber constructor(val number:String,
val tag:Int){
}
- KotlinPoker
package KotlinPoker
//花色
class PokerSuit constructor(val suit:String,val tag:Int) {
}
3.Poker
class Poker (val numberObj: PokerNumber, val suiObj:PokerSuit) {
fun compareTo(other: Poker)=
if (this.numberObj.tag == other.numberObj.tag)//点数相同比较花色
{
this.suiObj.tag > other.suiObj.tag
} else {
this.numberObj.tag > other.numberObj.tag
}
}
- PokerManager
import kotlin.random.Random
class PokerManager {
val pokers:ArrayList<Poker> = arrayListOf();
//点数数组
val numbers= arrayOf("3","4","5","6","7","8","9","10","J","Q","K","A","2")
val suis= arrayOf("♦","♣","❤","♠")
init {
for((i,number) in numbers.withIndex())
{
for((j,suit) in suis.withIndex())
{
pokers.add(Poker(PokerNumber(number,i), PokerSuit(suit,j)))
}
}
}
//获取一张扑克牌
fun getPoker():Poker{
// val index= Random.nextInt(pokers.size)
// val poker=pokers[index]
// pokers.removeAt(index)
// return poker
Random.nextInt(pokers.size).also {
pokers[it].apply {
pokers.remove(this)
return this
}
}
}
}
- 测试抽牌
fun main() {
PokerManager().apply {
val poker1=getPoker()
val poker2=getPoker()
val result=poker1.compareTo(poker2)
if(result)
{
println("${poker1.numberObj.number}${poker1.suiObj.suit}>"+"${poker2.numberObj.number}${poker2.suiObj.suit}")
}
else{
println("${poker1.numberObj.number}${poker1.suiObj.suit}<"+"${poker2.numberObj.number}${poker2.suiObj.suit}")
}
}
}
总结
学习面向对象的方式,并且理解题目事件、“人物”,并理解其关联十分重要,面向对象的设计在某种程度上能够让整个程序的逻辑性更强、比面向过程的程序更加的模块化,为了更加理解每个类的关系,我强烈建议认真学习画UML图,这一定会是你编程路上的一个十分实用的工具。