Nesne tabanlı programlama teknikleri hakkında

Başlatan strom, 12 Temmuz 2016, 20:39:00

strom

Herkese iyi günler.
Bir süredir nesne tabanlı programlama yöntemleri kafamı kurcalıyor. Normalde kodu bir şekilde yazıyoruz, bir şekilde çalışıyor, yeri geliyor oldukça güzelde çalışıyor ama dönüp koda baktığımda tek gördüğüm karmaşa. Estetikten ve teknikten çoğu zaman yoksun oluyor.
C++ ile kod yazıyorum ama function overloading dışında bir avantajını kullanamadım. Zaman zaman nesne tanımlamaya çalışsamda, bu işimi kolaylaştırmak yerine zorlaştırdı. Farkettim ki nesneleri sadece ilgili fonksiyonları ve değişkenleri tek bir yere toplama olarak görüyormuşum. Nesneler arası ilişkileri, problemi nesneler şeklinde ele almayı hiç beceremedim.
Son zamanlarda bu yöntemler hakkında araştırma yaptım ve gördüm ki C++ hakkında bildiğim veya bildiğimi sandığım şeyler sadece buzdağının görünen kısmıymış.. Derinlerde çok farklı bir disiplin ve teknik yatıyormuş. Sadece syntax öğrenmek ne yazık ki pek bir işe yaramıyormuş. Kalıtım, çokbiçimcilik gibi nesne tabanlı programlamanın temel taşlarını kavram olarak öğrendiğimde bu işi kaptığımı düşünmüştüm ama aslında gördüm ki bu kavramları öğrenmenin dilin syntax'ını öğrenmekten bir farkı yokmuş. Bu kavramlar bir çok programlama tekniğinin kapılarını açıyormuş ve bu teknikleri öğrenmeden nesne kullanmak sadece işimi dahada zorlaştırıyordu.
Konu ne yazık ki biraz dağılacak. Kafamda çok soru var ve bazen sorunun ne olduğunu kelimelere dökemiyorum. O yüzden sorularım biraz dağınık olabilir. Bunun için şimdiden özür dilerim.
Sorularıma başlarsak;
1-Interface neden kullanılıyor?
Bu işi öğrenmeye başladığımda en çok kafama takılan konu buydu. Tamamı virtual olan bir sınıf ne işe yarabilirdi ki? Yani bana herhangi bir faydası yokmuş gibi görüyordum. Sonuçta interface'ten türeyen bir sınıf tüm fonksiyonları kendi üretmek zorunda. Normal kalıtım mantığına biraz ters gelmişti.
Biraz araştırınca şöyle bir örneğe denk geldim; Mesela büyük bir yazılım şirketinde kod geliştiriyor olalım. Yazdığımız bu kod başka bir programcı veya programcılar tarafından kullanılacak. Atıyorum programın bitmesi 1 ay sürsün. Bu durumda ilk kodu geliştiren kişi, bunu kullanacak programcılara bir fonksiyon listesi veriyor (Bu bizim interface'imiz oluyor). Diğer kullanıcılar bu fonksiyonları kullanarak kendi programlarını geliştiriyorlar. Ancak daha henüz o fonksiyonların içi boş ama en azından ilk programcının yazdığı koda nasıl erişmeleri gerektiğini biliyorlar ve bu yüksek ihtimalle değişmeyecek. İki tarafta kendilerine ait kodlamayı yapıyorlar ve hiç kimse bir diğerinin yazdığı kodun içeriğini bilmek zorunda kalmıyor ayrıca kimse kimseyi beklemek zorunda kalmıyor ve işler paralel bir şekilde sürebiliyor.
Bunun yanısıra başka ne gibi kullanımlar olabileceğini düşündüm ve aklıma şu geldi;
Bir proje düşünelim. Mesela modbus protokolünü implement etmek isteyelim. İki tip modbus haberleşmesi olabilir. Bunlar ModbusRTU ve ModbusTCP. Bunlardan biri uart haberleşmesini kullanırken bir diğeri tcp ile haberleşiyor. İkisininde de komutlar ortak ama verinin gittiği ortam ve veriye eklenen header'lar farklı.
Bunun yanısıra birçok seri iletişim protokolleri var. Bunların hepsi için ayrı kodlama gereksede aslında hepsinin ortak kullanım metodları var. Örneğin Transmit, Receive metotları gibi.
Bu bilgilerden yola çıkarak bir tasarım yaptım. Bu tasarımda 2 tane Interface ve 2 tane bunlardan türeyen sınıf var.
1.Interface -> Seri protokol için kullanılacak interface. Bütün seri iletişim protokollerinde ortak kullanılan metotları barındıran bir kalıp gibi işlev görecek. Adına ISerialDriver dedim
#ifndef __ISERIALDRIVER_H
#define __ISERIALDRIVER_H

#include <stdint.h>
  
/**
 * @brief Type definitions for callback function
 */
typedef void (*AsyncCallback)(void*);

/**
 * @brief Virtual base class to derive all serial communication drivers
 */
class ISerialDriver
{
	public:
	
	//Blocking communication
	virtual void Transmit(uint8_t *data, uint32_t len) = 0;
	virtual void Receive(uint8_t *data, uint32_t len) = 0;
	//Non-Blocking communication
	virtual void BeginTransmit(uint8_t *data, uint32_t len, AsyncCallback fncCallback, void* param = 0) = 0;
	virtual void BeginReceive(uint8_t *data, uint32_t len, AsyncCallback fncCallback, void* param = 0) = 0;
	virtual void EndTransmit() = 0;
	virtual void EndReceive() = 0;
};

#endif


2.Interface -> Modbus iletişiminde kullanılan ortak metotlar için oluşturulmuş interface. Adına IModbus dedim. Not: Modbus protokolünü tamamen implement etmedim. Sadece göstermelik birkaç satır ekledim.
#ifndef __IMODBUS_H
#define __IMODBUS_H

#include "ISerialDriver.h"

class IModbus
{
	public:
	virtual void ReadInputRegister(uint16_t regAddr, uint16_t (data)[1]) = 0;
	virtual void ReadInputRegister(uint16_t regAddr, uint16_t (data)[1], AsyncCallback fncCallback) = 0;
	
	virtual void SetDriver(ISerialDriver* driver) = 0;
	virtual ISerialDriver* GetDriver() = 0;
};

#endif


1.Class -> Uart iletişimini gerçekleştirmek için UartDriver isimli bir class oluşturdum. Bu class ISerialDriver interface'inden türüyor ve bu interface'İn tüm metotlarını, uart protokolüne uygun olarak gerçekleştiriyor.
//=================================================================================================
// Section: Include
//=================================================================================================
#include <stdint.h>
#include "ISerialDriver.h"
#include "stm32f4xx.h"

//=================================================================================================
// Section: Defines that user can make change 
//=================================================================================================
#define UARTDRIVER_DEFUALT_TIMEOUT 1000

//=================================================================================================
// Section: External function and variable definitions
//=================================================================================================
extern UART_HandleTypeDef huart2; //Uart modülü ile iletisim kurmak için gerekli global degisken
																	//Hal kütüphanesinde usart için gerekli tüm fonksiyonlar için
																	//bu degisken parametre olarak gönderilir

//=================================================================================================
// Section: Class definition
//=================================================================================================
class UartDriver : virtual public ISerialDriver
{
 
	private:
		
	
	
	public:
		
	//
	// Constructor and Deconstuctor
	//
	UartDriver();
	
	//
	// Additional transmit and receive functions
	//
	void Transmit(const char * data); //Transmit string located at rom
	
	//
	// Virtual functions implementing the ISerialDriver base virtual class
	//
	virtual void Transmit(uint8_t *data, uint32_t len);
	virtual void Receive(uint8_t *data, uint32_t len);
	virtual void BeginTransmit(uint8_t *data, uint32_t len, AsyncCallback fncCallback, void* param = 0);
	virtual void BeginReceive(uint8_t *data, uint32_t len, AsyncCallback fncCallback, void* param = 0);
	virtual void EndTransmit();
	virtual void EndReceive();
	
	protected:
		
	//
	// Properties
	//
	private:
		//
		// Private member of class
		//
		uint32_t m_timeout;
		UART_HandleTypeDef* m_huart;
	public:
		// Accessor and mutator of private member of class
		void SetTimeout(uint32_t timeout);
		uint32_t GetTimeout();
	
		void SetUartHandle(UART_HandleTypeDef* uart);
		UART_HandleTypeDef* GetUartHandle();
	
};


2.Class -> ModbusRTU'yu gerçekleştirmek için oluşturduğum class. Bu class'ta IModbus interface'inden türüyor.
#ifndef __MODBUSRTU_H
#define __MODBUSRTU_H

#include "IModbus.h"

class ModbusRTU : virtual public IModbus
{
	public:
		
	//
	// Constructor and Deconstructor
	//
	
	//
	// Implementation of IModbus
	//
	
	virtual void ReadInputRegister(uint16_t regAddr, uint16_t (data)[1]);
	virtual void ReadInputRegister(uint16_t regAddr, uint16_t (data)[1], AsyncCallback fncCallback);
	virtual void SetDriver(ISerialDriver* driver);
	virtual ISerialDriver* GetDriver();
	
	//
	// Propterties
	//
	private:
		
	ISerialDriver* m_driver; //Properties of this variable is implemented in the IModbus interface
};

#endif


Şimdi bunlar arasında ilişkiyide şu şekilde tanımladım;

Not: Bu ilk UML diyagram çizim denemem. Hatalar olabilir.

Birde yapmak istediğimi yazılı anlatayım çünkü uml diyagramını bilmediğimden yapmak istediğimle çizdiğim farklı şeyler olabilir :)
UartDriver'ı ISerialDriver interface'inden türeyerek oluşturulmuş.
ModbusRTU class'ı IModbus classını kullanarak oluşturulmuş ve ISerialDriver interface'ini aggregation yolu ile kendine dahil etmiş.

Şimdide bunların kullanımına örnek vereyim;
UartDriver uart1;
ModbusRTU modbus1;
uint8_t ReceiveBuffer[100];
int i = 0;
void ReceiveHandler(void* obj)
{
	i++;
	uart1.EndReceive();
	uart1.BeginReceive(ReceiveBuffer, 6, ReceiveHandler);
}
...
...
...
	uart1.SetUartHandle(&huart2);
	uart1.BeginReceive(ReceiveBuffer, 6, ReceiveHandler);
	uart1.Transmit("Deneme");
	
	uint16_t send[1] = {100};
	modbus1.SetDriver(&uart1);
	modbus1.ReadInputRegister(65, send);


Aslında bu kodlar yapmak istediğim şeyi tam olarak yansıtamıyorlar ama en azından yaklaşıyor. Benim burda interface kullanmamım sebebi, mesela modbus1 nesnesi için SetDriver metodunu kullanarak istediğim seri iletişim yöntemi ile gönderme yapabilirim. Çünkü SetDriver metodu ISerialDriver interface'ine ait bir pointer parametresine sahip. Kalıtım mantığında ortak base classtan türeyen sınıflar, base class'a ait bir pointer'a atılabilir. Bütün seri iletişim protokolleride bu sınıftan türeyeceği için kodlarda hiçbir değişiklik yapmadan, sadece SetDriver fonksiyonunu çağırarak, modbus protokolünün istediğimiz seri iletişim protokolü ile gönderilmesini sağlayabilirim. Örneğin modbus1.SetDriver(&tcp1) deseydim ve tcp1'de ISerialDriver'dan türeyen bir sınıf olsaydı, başka hiçbir değişiklik yapmadan modbus1.ReadInputRegister(65, send); bu kod ile send datasını tcp üzerinden göndermiş olacaktım.

Sizce bu kurduğum mantık hatalı mı? Probleme yaklaşımım nesne tabanlı proglama ruhuna uygun mu? Benzer bir işlem OOP teknikleriyle nasıl işlenebilirdi?
Ayrıca interface kullanım sebebleri ile ilgili kaynak, kitap veya tecrübe paylaşabilecek birileri olursa çok mutlu olurum.

Daha sormak istediğim çok şey var ama şimdilik ancak bunu toparlayabildim.

İlgilenen, okuyan, vakit ayıran herkese sonsuz teşekkürler...

Not: Kodları Keil'de STM32F407 için HAL kütüphanesini kullanarak yazdım. Tüm kütüphaneyi upload etmek zor olduğundan sadece ilgili dosyaları upload ediyorum. İsteyen olursa tümünüde upload ederim
http://s4.dosya.tc/server2/hakbwq/ISerialDriver_Uart_v1.rar.html

bocek

Yapılmış örnekleri inceleyerek te bir çok şey öğrenebilirsiniz.

Mesela C++ ile mcu (stm32, avr) programlama konusunda çok güzel örnekleri şurada bulabilirsiniz:

http://andybrown.me.uk/2013/07/14/stm32plusnet/

Adam sistemli, düzenli ve açıklayıcı kod yazıyor, paylaşıyor. Soruları da cevaplıyor gördüğüm kadarıyla. Forumu filan var.
1 ya da 0. işte 'bit'ün mesele..

mufitsozen

#2
Alıntı yapılan: gerbay - 12 Temmuz 2016, 22:17:39
........
şimdi bu konuyu keşke @mufitsozen ağabey anlatsaydı..
........

Sevgili @gerbay mufit bilsede artik cok fazla bir sey anlatabilse keske. Neredeyse sabah kahvaltida yumurta yedimmi yemedimmi onu hatirlamakta gucluk cekiyorum.

Yinede akli melekelerimi daha fazla kaybetmeden hemen ilk aklima gelenleri belirteyim.

SW (daha da genis olcekte system)  Analysis ve Design icin kullanilan bir takim prensipler vardir. Bunlara dikkat etmeden yapilan tasarimlarda genellikle cok ciddi problemler (olcekte, karmasiklikta, bakim yapilabilirlikte vb - scalability & complexity & maintability)
cikacaktir.

Neyse sadede gelelim, bu prensiplerin cogunlugu genel olarak dogru oldugu gibi ozellikle Nesne tabanli tasarim yapma konusunda cok onemli olanlari vardir. Bunlarin ilk ve  en onemli olanlari S.O.L.I.D kisaltmasi ile isimlendirilmistir. @Karamel'i kusturmeselerdi asagida kisa ozetini yapacagim bu prensiplerin turkce tercumesini ve bunlarin daha detayli ve c++ icin olan orneklerinide yapardi ama, maalesef bu artik mumkun değil.

Uzun sozun kisasi SOLID su anlama geliyor:

  • S – Single-responsiblity principle
  • O – Open-closed principle
  • L – Liskov substitution principle
  • I – Interface segregation principle
  • D – Dependency Inversion Principle

Seninde bahsettigin "desgn patterns" bahsini bundan sonra ogrenmek daha dogru olacaktir.

Bunlari acikla demeden once ben kaciim :-)

picprojedeki arkadaslara da Allah zihin acikligi versin.

 
Aptalca bir soru yoktur ve hiç kimse soru sormayı bırakana kadar aptal olmaz.

strom

#3
Öncelikle herkese bu güzel cevapları için teşekkür ederim. Biraz yoğun olduğum için ancak dönebildim.

@gerbay
İnterface kullanımı pattern'lerin temel taşını oluşturuyor anlaşılan. Ayrıca kafamda oluşan "böyle bir programı nasıl tasarlarım" sorularının büyük çoğunluğu bu pattern'lerde mevcut sanırım. Tabi şu an için sadece okuyorak gidiyorum. Pratik uygulamalara uygulayabilecek miyim ya da gerçekten mevcut sorunuma doğru yaklaşımı öngörebilecek miyim bunu zaman gösterecek.

@mufitsozen
Hocam zaten beklediğim cevaplar açıklamalardan çok yönlendirme. Şu ana kadar da beklentilerim fazlasıyla karşılandı. Kod yazarken hissettiğim disiplin ve düzen eksiklikleri aslında başkaları tarafından çoktan sistematikleştirilmiş.
SOLID prensipleri anlaması kolay ama uygulaması epey bir tecrübe gerektiriyor sanırım. Şu ana kadar S ve O prensiplerine bakabildim.

@bocek
Verdiğin siteyi yakın zamanda bir arkadaşımda önermişti. Adamın projeleri ve paylaşımları çok ilginç. Böyle bir siteye nasıl daha önce denk gelmediğime hayret ettim. Ancak adamın kodlama stilini çözmek için en baştan verdiği örnekleri incelemek lazım. Gömülü sistemlerde sınıflara yaklaşımı benim için önemli bir konu. Ama biraz incelediğimde çokta aradığım gibi bir kodlama değildi gibi. Tabi biraz daha incelemek lazım. Hazır örnek olarak arduino kodlarını inceledim birazda. Gerçekten güzel yapmış adamlar. Daha önce hiç arduino kullanmadığıma pişman oldum. Hep bu ön yargılar işte.

Aslında şu an daha önce planladığım şey daha önce bitirmiş olduğum bir projeyi yeniden tasarlamak. Bitirme projem için 1 hafta için osiloskop ve spektrum analizör yapmam gerekiyordu. Tabi işin DSP ve elektronik kısımları proje için çok önemli değildi. Sadece ADC'den aldığım verileri ekrana bassam hoca için yeterli bir durumdu. Bu yüzden asıl uğraştığım şey bir arayüz oluşturmaktı.
Bu projeden önce bazı GUI componentlerini (Scrollbar, histogram (aslında component'in yaptığı iş histogramın tanımına uymuyor ama yine de görsel benzerliğinden dolayı bu ismi verdim), ..) daha önce yazmıştım. Daha sonra bunları geliştirip bir proje ortaya çıkardım. Ortaya çıkan ürün her ne kadar düzgün çalışsada programlamanın temel özelliklerinden yoksundu. Bunlar;
1- Geliştirmeye çok açık değildi. Hızlı bir şekilde bitirmek zorunda olduğumdan ve düzgün bir kodlama sitilim olmadığından programdaki bileşenlerin işlevleri hep birbirine girdi. Bu yüzden de yapacağım bir değişiklik için aslında birden fazla değişikliğe sebep oluyordu.
2- Yeniden kullanabiliriği düşüktü. Aslında kodlamaları mümkün olduğunca yeniden kullanabilirliğini düşünürek yazmıştım. En azından başlangıçta o şekildeydi. Daha sonra kodlar karmaşıklaştıkça biraz bağımlılık arttı.
Yani projeyi geliştirmek için önceki projeden devam etmektense yeniden düzgün bir şekilde gitmeye karar verdim. Tabi bunu yaparkende siz değerli büyüklerimin tavsiyelerine başvuracam. Çünkü projeyi gerçekleştirirken kafamda halihazırda bir takım sorular oluşmuştu. Bu soruların çözümüne yönelik tavsiyeler beni oldkuça hızlandırabilir.
Sorularıma geçmeden önce projemin fotolarından bazılarına buraya koyayım.
Spektrum analizör kısmı

Osiloskop kısmı

Spektrum ve osiloskop arasında tek tuşla geçiş yapabiliyoruz. İlk resimdeki spektrum aslında ikinci resimdeki dalganın spektrumu. Birde normal ölçüm cihazılarından farklı olarak dalgayı zamanda genişletebilmek için sabit ve yüksek bir örnekleme alıp, veri üzerinde pan yapmak yerine direk olarak sampling frekansını değiştirerek pan yapıyor :) Ama sorun değildi proje için.
Resimde görülen her alan aslında bir nesne. Verilerin çizildiği alan, textlerin çizildiği alan, butonlar, scrollbarlar filan hepsi ayrı birer nesne.
(Ek not: Yeri gelmişken design pattern'lere göz gezdirirken decorator patterne denk geldim. Mesela spektrumun çizildiği ekrandaki scrollbar'ı (spektrum çizildiği ekrana histogram adını verdim bu arada. İsimlendirmem biraz kötüdür :)) histogram classına nasıl bağlayacağıma tam olarak karar verememiştim. Bunun yanısıra her bir gui componentin border özelliği var. Bu özelliği bir flagta tutup, her draw çağrısında bu flag'ı kontrol edip ona göre uygun çizimi yaptırıyordum. Bu da tabi kodu biraz karışıtırıyordu. Decorator pattern aslında benzer durumlar için tasarlanmış. Bir class'a ek özellik eklemeyi sistemleştirmiş. Benim durumuma uygun mu bunu zamanı gelince göreceğim ama GoF'un kitabında decorator patterni için verilen örnek tam aradığım şeydi :))

Aslında soracağım çok soru var. Konuyu çok dallandırmamak için en temel sorumu sormak istiyorum. Diğer sorularımı yeri geldikçe sorarım.

Soru: En büyük problemim grafik nesnesini nasıl tanımlamam gerektiğiydi. Aslında projeye başlarken önce grafik kütüphanesini nesne olarak oluşturmuştum ama daha sonra diğer componentlerde bu class'ı nasıl kullacağıma karar veremediğimden (kalıtımla mı aktarsam, compositionla mı bağlasam yoksa grafik nesnesini statik mi yapsam gibi) en sonunda graphic.cpp adında bir modül oluşturup çizimle ilgili bütün fonksiyonları burda toplamaya karar vermiştim. Her component veya herhangi bir fonksiyon burdaki çizim fonksiyonlarını çağırarak (mesela DrawLine gibi) çizim yapabiliyor.
Yeni kodumda temel olarak herşeyi nesne olarak tanımlamak istiyorum. Bu yüzden de önce grafik nesnesini nasıl kullanmam gerektiğine karar vermem gerekti.
Önce hazırladığım grafik kütüphanesini buraya ekleyeyim;
Graphics.h
#ifndef __GRAPHICS_H
#define __GRAPHICS_H

#include "stm32f4xx.h"
#include "ili9341.h"
#include "fonts.h"



typedef uint16_t COLOR;

struct POINT
{
	uint16_t X;
	uint16_t Y;
};

struct PIXEL
{
	double x;
	double y;
};

struct RECT
{
  uint16_t x;
  uint16_t y;
  uint16_t width;
  uint16_t height;
};


#define RGB565

#ifdef	RGB565
#define RGB(Red,  Green, Blue) ((COLOR) ( (Red & 0x1F) << 11) | ((Blue & 0X1F) << 0)|  ((Green & 0x3F) << 5 ))
#endif


extern COLOR Background;
extern uint8_t ScreenRotation;
//Output Primitives
void SetCursor(COLOR c, uint16_t x, uint16_t y, uint8_t clear = 0);
void SetPixel(COLOR c, uint16_t x, uint16_t y);
COLOR GetPixel(uint16_t x, uint16_t y);
void DrawLine(COLOR c, int x0, int y0, int x1, int y1, int dash = 0 );	
void DrawRect(COLOR c, int16_t x, int16_t y, uint16_t width, uint16_t height, uint8_t fill = 0);
void DrawRect(COLOR c, RECT &rect, uint8_t fill = 0);
void DrawBar(COLOR c, uint16_t x, uint16_t y, uint16_t length, uint16_t height);
void SetBackground(COLOR c);
void SetRotation(uint8_t rot);
uint8_t CheckIsRect(RECT rect, uint16_t x, uint16_t y);


#define WHITE 0xFFFF
#define BLACK	0x0000
#define RED		RGB(0xFF, 0, 0)
#define GREEN	RGB(0, 0xFF, 0)
#define BLUE  RGB(0, 0, 0xFF)


#define SCREEN_ROTATION_VERTICAL 0
#define SCREEN_ROTATION_HORIZONTAL 1


#endif

Graphics.cpp
#include "Graphics.h"
#include <stdio.h>


#define abs(x)  ( (x<0) ? -x : x )

uint16_t LCD_BUFFER[240 * 320];
uint8_t ScreenRotation = 0;
COLOR Background;

void SetPixel(COLOR c, uint16_t x, uint16_t y)
{
	if(!ScreenRotation){
		if(x < LCD_WIDTH && y < LCD_HEIGHT)
			LCD_BUFFER[x + (y * LCD_WIDTH)] = c;
		}
	else
	{
    if(x < LCD_WIDTH && y < LCD_HEIGHT)
		{
		int temp = y;
		y = x;
		x = LCD_HEIGHT - temp;
		
		

			LCD_BUFFER[x + (y * LCD_HEIGHT)] = c;
		}	
	}
}

void SetCursor(COLOR c, uint16_t x, uint16_t y, uint8_t clear)
{
  if(clear == 0)
  {
    DrawLine(c, x, 0, x, LCD_HEIGHT);
    DrawLine(c, 0, y, LCD_WIDTH, y); 
  }
  else
  {
    DrawLine(Background, x, 0, x, LCD_HEIGHT);
    DrawLine(Background, 0, y, LCD_WIDTH, y);     
  }
}

COLOR GetPixel(uint16_t x, uint16_t y)
{
	if(!ScreenRotation){
		if(x < LCD_WIDTH && y < LCD_HEIGHT)
			return LCD_BUFFER[x + (y * LCD_WIDTH)];
		}
	else
	{
		int temp = y;
		y = x;
		x = LCD_HEIGHT - temp;
		
		
		if(x < LCD_HEIGHT && y < LCD_WIDTH)
		{
			return LCD_BUFFER[x + (y * LCD_HEIGHT)];
		}	
	}	
  
  return 0;
}


uint8_t CheckIsRect(RECT rect, uint16_t x, uint16_t y)
{
  if(x >= (rect.x) && x <= (rect.x + rect.width) && y >= rect.y && y <= (rect.y + rect.height))
    return 1;
  else
    return 0;
}

void SetRotation(uint8_t rot)
{
	if(rot == 1)
	{
		ScreenRotation = 1;
		LCD_HEIGHT = 240;
		LCD_WIDTH = 320;
	}
	else
	{
		ScreenRotation = 0;
		LCD_HEIGHT = 320;
		LCD_WIDTH = 240;
	}
}



void DrawRect(COLOR c, int16_t x, int16_t y, uint16_t width, uint16_t height,uint8_t fill)
{
  if(width <= 1 || height <= 1) return; 
  width--;          
  
	if(fill)
	{
		for(int i = 0; i < height; i++)
		{
			DrawLine(c, x, y + i, x + width, y + i);
		}
	}
	else
	{
		DrawLine(c, x, y, x + width, y);
		DrawLine(c, x, y, x, y + height);
		DrawLine(c, x, y + height, x + width, y + height);
		DrawLine(c, x + width, y, x + width, y + height);
	}
}

void DrawRect(COLOR c, RECT &rect, uint8_t fill)
{
  DrawRect(c, rect.x, rect.y, rect.width, rect.height, fill);
}

void DrawLineDash(COLOR color, int startx, int starty, int endx, int endy) 
{

}

void DrawLine(COLOR color, int startx, int starty, int endx, int endy, int dash) {
    int t, distance, j = 0, isPlot = 1;
    int xerr=0, yerr=0, delta_x, delta_y;
    int incx, incy;
 
    /* compute the distances in both directions */
    delta_x=endx-startx;
    delta_y=endy-starty;
 
    /* Compute the direction of the increment,
       an increment of 0 means either a horizontal or vertical
       line.
    */
    if(delta_x>0) incx=1;
    else if(delta_x==0) incx=0;
    else incx=-1;
 
    if(delta_y>0) incy=1;
    else if(delta_y==0) incy=0;
    else incy=-1;
 
    /* determine which distance is greater */
    delta_x=abs(delta_x);
    delta_y=abs(delta_y);
    if(delta_x>delta_y) distance=delta_x;
    else distance=delta_y;
 
    /* draw the line */
    for(t=0; t<=distance+1; t++) {
			if(!dash)
        SetPixel(color, startx, starty);
			else
			{
				if(isPlot)
				{
					SetPixel(color, startx, starty);
				}
				if(j < 3)
					isPlot = 1;
				else
					isPlot = 0;
				if(j == 6)
				{
					j = 0;
					isPlot = 1;
				}
				j++;
			}
         
        xerr+=delta_x;
        yerr+=delta_y;
        if(xerr>distance) {
            xerr-=distance;
            startx+=incx;
        }
        if(yerr>distance) {
            yerr-=distance;
            starty+=incy;
        }
    }
}

void DrawBar(COLOR c, uint16_t x, uint16_t y, uint16_t length, uint16_t height)
{
  for(uint16_t i = 0; i < length; i++)
  {
    DrawLine(c, x + i, y, x + i, y - height);
  }
  
}

void SetBackground(COLOR c)
{
  Background = c;
	for(int y = 0; y < LCD_HEIGHT; y++)
	{
		for(int x  = 0; x < LCD_WIDTH; x++)
		{
			SetPixel(c, x, y);
		}
			
	}
	

}


Böyle bir yazım tarzında şu sorunlar çıkıyor;
1-Mesela ScreenRotation değişkeni global bir değişken. Değeri değiştiğinde, değişimden sonra yapılan tüm çizimlerin yönü değişiyor.
2-SetPixel fonksiyonu içerisinde sınır kontrolü yapılıyor. Ekranın tamamı için tanımlanan buffer'In dışına çıkmayacak şekilde, girilen koordinatlar kontrol ediliyor. Ama mesela ben şunu istiyorum; bir buton sınıfım var. Butona yazı yazdığımda yazdığım yazının buton sınırları dışına çıkmasını istemiyorum. Bu durumda sınır değer kontrolünü hem buton için hem de SetPixel içerisinde array için yapmış oluyorum. Bu karışıklığa sebep oluyor.

C# gibi veya diğer grafik kütüphaneleri nasıl çalışıyor? Tabi embedded üzerinde kod geliştirdiğimi varsayarsak nasıl bir yaklaşım izlesem daha doğru olur? Örnek olabilecek, rahat okunabilen açık kaynak projeler varsa bildiğiniz onlarda çok iyi olur. Tabi biraz anlaşılır olsa güzel olur. Geçenlerde RapidJSON isimli bir JSON parser kodunu inceleyeyim dedim, başka bir dille yazılmış gibi geldi. Anlamak çok güçtü.

Umarım kendimi anlatabilmişimdir. İlginiz için tekrardan teşekkürler

mufitsozen

#4
Alıntı yapılan: strom - 15 Temmuz 2016, 19:51:42
@mufitsozen
Hocam zaten beklediğim cevaplar açıklamalardan çok yönlendirme. Şu ana kadar da beklentilerim fazlasıyla karşılandı. Kod yazarken hissettiğim disiplin ve düzen eksiklikleri aslında başkaları tarafından çoktan sistematikleştirilmiş.
SOLID prensipleri anlaması kolay ama uygulaması epey bir tecrübe gerektiriyor sanırım. Şu ana kadar S ve O prensiplerine bakabildim.


Mesleki merak ve ogrenmeye, kendinizi gelistirmeye duydugunuz ickin (eski dilde mündemiç, ingilizce intrinsic) motivasyona hayran oldugumu belirtmek isterim. Bu cabaniz cok takdire sayan. Ben artik fiilen muhendislik yapmadigim icin size guncel ve pratik bilgiler maalesef veremiyorum, ama ne aradiginizin farkina varmaniz size yeteri, kadar rehber olacaktir.

Kendinize cok baski yapmayin, bu ilgi ve isteginizl surdururseniz zaman icinde isinizi etkileyecek metod ve araclari belirler, hatta yeni araclar gelistirir bunlari kullanir ve is kalitesini artirirsiniz.  Sizden once bu yoldan gecenlerin tecrube ve tavsiyelerinden faydalanmakta cok akillica bir tutum. Kitaplar ve etrafinizda basarili is cikaranlarin urunleri ve calismalarindan da faydalanmak size zaman kazandirir.

Benim bu konuda size tavsiyem cok acele etmeyin, bu isler zamanla olur. Mukemmel cozumler uretmeye calismayin. "Perfect is the enemy of good" :) Bir muhendis olarak cozumler uretin, mukemmel cozumler yapmaya calismayin. Bir muhendis olarak cozumlerinizin musterinizin kabul edecegi kalitede olmasi yeterlidir. Kenar susu yapmaya, yada "over-engineered" sistemler yapmamaya dikkat edin.

Alıntı yapılan: strom - 15 Temmuz 2016, 19:51:42

Böyle bir yazım tarzında şu sorunlar çıkıyor;
1-Mesela ScreenRotation değişkeni global bir değişken. Değeri değiştiğinde, değişimden sonra yapılan tüm çizimlerin yönü değişiyor.
2-SetPixel fonksiyonu içerisinde sınır kontrolü yapılıyor. Ekranın tamamı için tanımlanan buffer'In dışına çıkmayacak şekilde, girilen koordinatlar kontrol ediliyor. Ama mesela ben şunu istiyorum; bir buton sınıfım var. Butona yazı yazdığımda yazdığım yazının buton sınırları dışına çıkmasını istemiyorum. Bu durumda sınır değer kontrolünü hem buton için hem de SetPixel içerisinde array için yapmış oluyorum. Bu karışıklığa sebep oluyor.

Bu yazdiklarinizdan kullandiginiz dillerdeki "variable scope" ve "variable storage lifetime" konularini tam olarak detayli bir sekilde bilmediginiz/kullanmadiginizi dusundum.
C++ icin birde namespace bahsi var.

Bunlari tekrar tekrar inceleyip, denemeler yaparak ogreniniz ve uygulayiniz. Bahsettiginiz seyler sorun değilde dogru tasarlanmamis sistem hatalari. 

Alıntı yapılan: strom - 15 Temmuz 2016, 19:51:42

C# gibi veya diğer grafik kütüphaneleri nasıl çalışıyor? Tabi embedded üzerinde kod geliştirdiğimi varsayarsak nasıl bir yaklaşım izlesem daha doğru olur? Örnek olabilecek, rahat okunabilen açık kaynak projeler varsa bildiğiniz onlarda çok iyi olur. Tabi biraz anlaşılır olsa güzel olur. Geçenlerde RapidJSON isimli bir JSON parser kodunu inceleyeyim dedim, başka bir dille yazılmış gibi geldi. Anlamak çok güçtü.


Kutuphaneler deyince en onemli ve acil konu kutuphaneyi programlamak değildir. Cunku bir kutuphanede ilk olarak stabilize olan(dusunulen, tasarlanan ve dis dunyaya aciklanan ve bundan dolayi degistirilmsei cok zahmetli olan) kutuphanenin "interface"dir. Buna genel olarak API diyoruz. Bu konuda cok guzel kitaplar var faydalanmaniz gereken. Bunlari bulup en azindan bir kere okuyun (bence temel kitaplar oldugu icin amazon'dan kitaplarini alin, ogrenci iseniz ve bulmak isterde bulamazsaniz bana haber verin)
https://www.amazon.com/API-Design-C-Martin-Reddy/dp/0123850037/ref=sr_1_1?s=books&ie=UTF8&qid=1468613612&sr=1-1&keywords=API+Design+for+C%2B%2B
https://www.amazon.com/Build-APIs-You-Wont-Hate/dp/0692232699/ref=sr_1_1?s=books&ie=UTF8&qid=1468613664&sr=1-1&keywords=API+Design

birde internet'de "What Makes APIs Difficult to Use" diye ararsaniz bulacaginiz guzel bir kac makale var size fikir verebilecek.

Calismalarinizda bol sans.

picproje'de gormeyi ozledigimiz bir istek ve ogrenme iceren bu paylasimlarinizdan dolayi da sizi tebrik ederim. Umarim bu parilti foruma biraz can verir.

Kolay gelsin.
Aptalca bir soru yoktur ve hiç kimse soru sormayı bırakana kadar aptal olmaz.