Γλώσσα Προγραμματισμού ΙΙ

Διάλεξη 5ης Μαΐου 2015 - Κλάσεις και αντικείμενα

Παραθέτουμε μερικά ακόμα χρήσιμα παραδείγματα προγραμμάτων που χρησιμοποιούν την τεχνική του αντικειμενοστραφούς προγραμματισμού. Στο πρώτο από αυτά αναπτύσσουμε ένα πρόγραμμα που θα μπορούσε να χρησιμοποιηθεί από μια εταιρεία διαχείρισης μεταχειρισμένων αυτοκινήτων. Θα υποθέσουμε ότι τα αυτοκίνητα της εταιρείας χωρίζονται σε τρεις τύπους: επιβατικά, φορτηγά και οχήματα τύπου SUV. Άσχετα με τον τύπο, η εταιρεία καταγράφει τα εξής δεδομένα για κάθε όχημα: μάρκα, έτος κυκλοφορίας, χιλιόμετρα που έχει διανύσει και τιμή. Ειδικά για τα επιβατικά αυτοκίνητα η εταιρεία καταγράφει των αριθμό των θυρών (2 ή 4), για τα φορτηγά τον τύπο μετάδοσης της κίνησης και για τα SUV την χωρητικότητα των επιβατών. Η τακτική που θα ακολουθήσουμε θα είναι η εξής: θα γράψουμε την κλάση Vehicle (όχημα) και της υποκλάσεις Car, Truck και SUV για κάθε συγκεκριμένο τύπο οχήματος οι οποίες κληρονομούν από την κλάση Vehicle τα κοινά χαρακτηριστικά κάθε οχήματος.


class Vehicle:
	def __init__(self, make, model, mileage, price):
		self.make = make
		self.model = model
		self.mileage = mileage
		self.price = price

	def getMake(self):
		return self.make

	def getModel(self):
		return self.model

	def getMileage(self):
		return self.mileage

	def setPrice(self, price):
		self.price = price

	def getPrice(self):
		return self.price

Ορίζουμε τώρα την υποκλάση Car (επιβατικό αυτοκίνητο) ως υποκλάση της Vehicle και προσθέτουμε το χαρακτηριστικό του αριθμού των θυρών:


class Car(Vehicle):
	def __init__(self, make, model, mileage, price, doors):
		Vehicle.__init__(self, make, model, mileage, price)
		self.doors = doors

	def getDoors(self):
		return self.doors

Με τον ίδιο τρόπο, ορίζουμε τις κλάσεις Truck και SUV, προσθέτοντας κάθε φορά τα ιδιαίτερα χαρακτηριστικά τους:


class Truck(Vehicle):
	def __init__(self, make, model, mileage, price, drive):
		Vehicle.__init__(self, make, model, mileage, price)
		self.drive = drive

	def getDrive(self):
		return self.drive

class SUV(Vehicle):
	def __init__(self, make, model, mileage, price, seats):
		Vehicle.__init__(self, make, model, mileage, price)
		self.seats = seats

	def getSeats(self):
		return self.seats

Ένα παράδειγμα χρήσης των κλάσεων αυτών φαίνεται παρακάτω:


bmw = Car("BMW", 2001, 7000, 15000.0, 4)
toyota = Truck("Toyota', 2000, 40000, 12000.0, "4WD")
volvo = SUV('Volvo', 2001, 30000, 18500.0, 5)

print 'Make: ' + car.getMake()
print 'Year: ' + car.getModel()
print 'Miles: ' + car.getMileage()
print 'Doors: ' + car.getDoors()
print 'Price: ' + getPrice()

Σαν δεύτερο παράδειγμα, αναπτύσσομε κλάσεις που θα μπορούσαν χρησιμοποιηθούν για την διαχείριση μιας μουσικής συλλογής.


class Song:
	def __init__(self, title, artist, album, track):
		self.title = title
		self.artist = artist
		self.album = album
		self.track = track

		artist.addSong(self)

class Album:
	def __init__(self, title, artist, year):
		self.title = title
		self.artist = artist
		self.year = year

		self.tracks = []

		srtist.addAlbum(self)

	def addTrack(self, title, artist=None):
		if artist is None:
			artist = self.artist

		numtracks = len(self.trucks)
		song = Song(title, title, artist, self, numtracks)

		self.tracks.append(song)

class Artist:
	def __init__(self, name):
		self.name = name

		self.albums = []
		self.songs = []

	def addAlbum(self, album):
		self.albums.append(album)

	def addSong(self, song):
		self.songs.append(song)


class Playlist:
	def __init__(self, name):
		self.name = name
		self.songs = []

	def addSong(self, song):
		self.songs.append(song)

band = Artist("Bob's Awesome Band")
album = Album("Bob's First Single", band, 2013)
album.add_track("A Ballad about Cheese")
album.addTrack("A Ballad about Cheese (dance remix)")
album.addTrack("A Third Song to Use Up the Rest of the Space")

playlist = Playlist("My Favourite Songs")

for song in album.tracks:
	playlist.add_song(song)

Το πρόβλημα του σακιδίου

Ένας διαρρήκτης έχει ένα σακίδιο το οποίο χωράει 20 κιλά. Σκοπός του είναι κάθε φορά που ληστεύει ένα σπίτι να παίρνει μαζί του τα πολυτιμότερα αντικείμενα χωρίς το συνολικό βάρος τους να υπερβαίνει την χωρητικότητα του σακιδίου. Ας υποθέσουμε για παράδειγμα ότι έχει να επιλέξει μεταξύ των παρακάτω αντικειμένων (δίπλα στο όνομα κάθε αντικειμένου αναγράφεται η αξία του, το βάρος του και η αξία του αντικειμένου ανά μονάδα βάρους):

Η ερώτηση είναι, βεβαίως, ποιά από τα παραπάνω αντικείμενα πρέπει να επιλέξει έτσι ώστε να μεγιστοποιήσει την αξία των κλοπιμαίων και χωρίς να υπερβεί την χωρητικότηατα του σακιδίου του. Ο διαρρήκτης μπορεί να καταστρώσει την εξής στρατηγική για την επιλογή των κλοπιμαίων που θα βάλει στο σακίδιό του: να επιλέξει πρώτα το "καλύτερο" αντικείμενο, στη συνέχεια το δεύτερο "καλύτερο" αντικείμενο κ.ο.κ., μέχρι να εξαντλήσει τη χωρητικότητα του σακιδίου. Πρέπει, φυσικά, να επιλέξει την έννοια του όρου "καλύτερο": είναι το αντικείμενο με την μεγαλύτερη αξία, το αντικείμενο με το μικρότερο βάρος ή το αντικείμενο με τον μεγαλύτερο λόγο αξίας προς μονάδα βάρους; Μπορούμε να γράψουμε μερικές γραμμές κώδικα οι οποίες θα βοηθήσουν το διαρρήκτη να αποφασίσει:


class Item:
	def __init__(self, name, value, weight):
		self.name = name
		self.value = float(value)
		self.weight = float(weight)

	def getName(self):
		return self.name

	def getValue(self):
		return self.value

	def getWeight(self):
		return self.weight

	def __str__(self):
		return self.name + ', ' + str(self.value) + ', ' + str(self.weight)

def value(item):
	retrun item.getValue()

def weightInverse(item):
	return 1.0 / item.getWeight()

def ratio(item):
	return item.getValue() / item.getWeight()

def buildItems():
	name = ['clock', 'painting', 'radio', 'vase', 'book', 'computer']
	values = [175, 90, 20, 50, 10, 200]
	weights = [10, 9, 4, 2, 1, 20]

	Items = []
	for i in range(len(values)):
		Items.append( Item(names[i], values[i], weights[i]) )

	return Items

Μπορούμε τώρα να εξετάσουμε ποιά από τις τρεις στρατηγικές που αναφέραμε παραπάνω θα είναι η περισσότερο επικερδής για το διαρρήκτη:


def greedy(Items, maxWeight, keyFcn):
	ItemsCopy = sorted(Items, key=keyFcn, reverse=True)

	totalValue = 0.0
	totalWeight = 0.0
	result = []
	i = 0

	while totalWeight < maxWeight and i < len(Items):
		if totalWeight + ItemsCopy[i].getWeight() <= maxWeight:
			result.append(ItemsCopy[i])
			totalWeight += ItemsCopy[i].getWeight()
			totalValue += ItemsCopy[i].getValue()
		i += 1

	return (result, totalVal)

def testGreedy(Items, constraint, getKey):
	taken, val = greedy(Items, constraint, getKey)
	print 'Total value of items = ' + str(val)
	for item in taken:
		print item

def testAll(maxWeight = 20):
	Items = buildItems()

	print 'use value strategy'
	testGreedy(Items, maxWeight, value)

	print 'use inverse weight strategy'
	testGreedy(Items, maxWeight, weightInverse) 

	print 'use value per unit weight strategy'
	testGreedy(Items, maxWeight, ratio) 

Αν καλέσουμε την συνάρτηση testAll() τα αποτελέσματα θα είναι τα ακόλουθα:


use value strategy
Total value of items = 200.0
computer, 200.0, 20.0

use inverse weight strategy
Total value of items = 170.0
book, 10.0, 1.0
vase, 50.0, 2.0
radio, 20.0, 4.0
painting, 90.0, 9.0

use value per unit weight strategy
Total value of items = 255.0
vase, 50.0, 2.0
clock, 175.0, 10.0
book, 10.0, 1.0
radio, 20.0, 4.0